js之 for循环
普通for 循环
语法
for ([initialization]; [condition]; [final-expression]) statement
initialization
- 一个表达式 (包含赋值语句) 或者变量声明。典型地被用于初始化一个计数器。该表达式可以使用
var
关键字声明新的变量。初始化中的变量不是该循环的局部变量,而是与for
循环处在同样的作用域中。该表达式的结果无意义。 condition
- 一个条件表达式被用于确定每一次循环是否能被执行。如果该表达式的结果为true,
statement
将被执行。 这个表达式是可选的。如果被忽略,那么就被认为永远为真。如果计算结果为假,那么执行流程将被跳到for
语句结构后面的第一条语句。 final-expression
- 每次循环的最后都要执行的表达式。执行时机是在下一次
condition
的计算之前。通常被用于更新或者递增计数器变量。 statement
- 只要
condition
的结果为true就会被执行的语句。 要在循环体内执行多条语句,使用一个块语句({ ... }
)来包含要执行的语句。没有任何语句要执行,使用一个空语句(;
)。
事例
for (var i = 0; i < 9; i++) { console.log(i); // more statements }
for await...of
语法
for await (variable of iterable) { statement }variable
在每次迭代时,将不同属性的值分配给变量。iterable
迭代其可迭代属性的对象。
事例:
async function* asyncGenerator() { var i = 0; while (i < 3) { yield i++; } } (async function() { for await (num of asyncGenerator()) { console.log(num); } })(); // 0 // 1 // 2
for each...in
语法
for each (variable in object) { statement }
variable
- 用来遍历属性值的变量,前面的
var
关键字是可选的.该变量是函数的局部变量而不是语句块的局部变量.
object
- 该对象的属性值会被遍历.
statement
- 遍历属性值时执行的语句. 如果想要执行多条语句, 请用(
{ ... }
) 将多条语句括住.
事例:
var sum = 0; var obj = {prop1: 5, prop2: 13, prop3: 8}; for each (var item in obj) { sum += item; } print(sum); // 输出"26",也就是5+13+8的值
警告:永远不要使用for each...in语句遍历数组,仅用来遍历常规对象
for...in
语法
for (variable in object) {...}
variable
- 在每次迭代时,将不同的属性名分配给变量。
object
- 被迭代枚举其属性的对象
-
描述
for...in
循环只遍历可枚举属性。像Array
和Object
使用内置构造函数所创建的对象都会继承自Object.prototype
和String.prototype
的不可枚举属性,例如String
的indexOf()
方法或Object
的toString()
方法。循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性(更接近原型链中对象的属性覆盖原型属性)。删除,添加或者修改属性
for...in
循环以任意序迭代一个对象的属性(请参阅delete
运算符,了解为什么不能依赖于迭代的表面有序性,至少在跨浏览器设置中)。如果一个属性在一次迭代中被修改,在稍后被访问,其在循环中的值是其在稍后时间的值。一个在被访问之前已经被删除的属性将不会在之后被访问。在迭代进行时被添加到对象的属性,可能在之后的迭代被访问,也可能被忽略。通常,在迭代过程中最好不要在对象上进行添加、修改或者删除属性的操作,除非是对当前正在被访问的属性。这里并不保证是否一个被添加的属性在迭代过程中会被访问到,不保证一个修改后的属性(除非是正在被访问的)会在修改前或者修改后被访问,不保证一个被删除的属性将会在它被删除之前被访问。数组迭代和
for...in
提示:
for...in
不应该用于迭代一个Array
,其中索引顺序很重要。数组索引只是具有整数名称的枚举属性,并且与通用对象属性相同。不能保证
for ... in
将以任何特定的顺序返回索引。for ... in
循环语句将返回所有可枚举属性,包括非整数类型的名称和继承的那些。因为迭代的顺序是依赖于执行环境的,所以数组遍历不一定按次序访问元素。因此当迭代访问顺序很重要的数组时,最好用整数索引去进行
for
循环(或者使用Array.prototype.forEach()
或for...of
循环)。仅迭代自身的属性
如果你只要考虑对象本身的属性,而不是它的原型,那么使用
getOwnPropertyNames()
或执行hasOwnProperty()
来确定某属性是否是对象本身的属性(也能使用propertyIsEnumerable
)。或者,如果你知道不会有任何外部代码干扰,您可以使用检查方法扩展内置原型。事例
var obj = {a:1, b:2, c:3}; for (var prop in obj) { console.log("obj." + prop + " = " + obj[prop]); } // Output: // "obj.a = 1" // "obj.b = 2" // "obj.c = 3"
var triangle = {a: 1, b: 2, c: 3}; function ColoredTriangle() { this.color = 'red'; } ColoredTriangle.prototype = triangle; var obj = new ColoredTriangle(); for (var prop in obj) { if (obj.hasOwnProperty(prop)) { console.log(`obj.${prop} = ${obj[prop]}`); } } // Output: // "obj.color = red"
for...of
语法
for (variable of iterable) { //statements }variable
在每次迭代中,将不同属性的值分配给变量。iterable
被迭代枚举其属性的对象。
示例
迭代Array
let iterable = [10, 20, 30]; for (let value of iterable) { value += 1; console.log(value); } // 11 // 21 // 31
如果你不想修改语句块中的变量 , 也可以使用const
代替let
。
let iterable = [10, 20, 30]; for (const value of iterable) { console.log(value); } // 10 // 20 // 30
迭代String
let iterable = "boo"; for (let value of iterable) { console.log(value); } // "b" // "o" // "o"
迭代 TypedArray
节
let iterable = new Uint8Array([0x00, 0xff]); for (let value of iterable) { console.log(value); } // 0 // 255
迭代Map
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]); for (let entry of iterable) { console.log(entry); } // ["a", 1] // ["b", 2] // ["c", 3] for (let [key, value] of iterable) { console.log(value); } // 1 // 2 // 3
迭代 Set
let iterable = new Set([1, 1, 2, 2, 3, 3]); for (let value of iterable) { console.log(value); } // 1 // 2 // 3
迭代 arguments
对象
(function() { for (let argument of arguments) { console.log(argument); } })(1, 2, 3); // 1 // 2 // 3
迭代 DOM 集合
迭代 DOM 元素集合,比如一个NodeList
对象:下面的例子演示给每一个 article 标签内的 p 标签添加一个 "read
" 类。
//注意:这只能在实现了NodeList.prototype[Symbol.iterator]的平台上运行 let articleParagraphs = document.querySelectorAll("article > p"); for (let paragraph of articleParagraphs) { paragraph.classList.add("read"); }
关闭迭代器
对于for...of
的循环,可以由break
, throw
或return
终止。在这些情况下,迭代器关闭。
function* foo(){ yield 1; yield 2; yield 3; }; for (let o of foo()) { console.log(o); break; // closes iterator, triggers return }
迭代生成器
你还可以迭代一个生成器:
function* fibonacci() { // 一个生成器函数 let [prev, curr] = [0, 1]; for (;;) { // while (true) { [prev, curr] = [curr, prev + curr]; yield curr; } } for (let n of fibonacci()) { console.log(n); // 当n大于1000时跳出循环 if (n >= 1000) break; }
不要重用生成器
生成器不应该重用,即使for...of
循环的提前终止,例如通过break
关键字。在退出循环后,生成器关闭,并尝试再次迭代,不会产生任何进一步的结果。
var gen = (function *(){ yield 1; yield 2; yield 3; })(); for (let o of gen) { console.log(o); break;//关闭生成器 } //生成器不应该重用,以下没有意义! for (let o of gen) { console.log(o); }
迭代其他可迭代对象
你还可以迭代显式实现可迭代协议的对象:
var iterable = { [Symbol.iterator]() { return { i: 0, next() { if (this.i < 3) { return { value: this.i++, done: false }; } return { value: undefined, done: true }; } }; } }; for (var value of iterable) { console.log(value); } // 0 // 1 // 2
for...of
与for...in
的区别
无论是for...in
还是for...of
语句都是迭代一些东西。它们之间的主要区别在于它们的迭代方式。
for...in
语句以原始插入顺序迭代对象的可枚举属性。
for...of
语句遍历可迭代对象定义要迭代的数据。
以下示例显示了与Array
一起使用时,for...of
循环和for...in
循环之间的区别。
Object.prototype.objCustom = function() {}; Array.prototype.arrCustom = function() {}; let iterable = [3, 5, 7]; iterable.foo = 'hello'; for (let i in iterable) { console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom" } for (let i in iterable) { if (iterable.hasOwnProperty(i)) { console.log(i); // logs 0, 1, 2, "foo" } } for (let i of iterable) { console.log(i); // logs 3, 5, 7 } Object.prototype.objCustom = function() {}; Array.prototype.arrCustom = function() {}; let iterable = [3, 5, 7]; iterable.foo = 'hello';
每个对象将继承objCustom
属性,并且作为Array
的每个对象将继承arrCustom
属性,因为将这些属性添加到Object.prototype
和Array.prototype
。由于继承和原型链,对象iterable
继承属性objCustom
和arrCustom
。
for (let i in iterable) { console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom" }
此循环仅以原始插入顺序记录iterable
对象的可枚举属性。它不记录数组元素3
, 5
, 7
或hello
,因为这些不是枚举属性。但是它记录了数组索引以及arrCustom
和objCustom
。如果你不知道为什么这些属性被迭代,array iteration and for...in
中有更多解释。
for (let i in iterable) { if (iterable.hasOwnProperty(i)) { console.log(i); // logs 0, 1, 2, "foo" } }
这个循环类似于第一个,但是它使用hasOwnProperty()
来检查,如果找到的枚举属性是对象自己的(不是继承的)。如果是,该属性被记录。记录的属性是0
, 1
, 2
和foo
,因为它们是自身的属性(不是继承的)。属性arrCustom
和objCustom
不会被记录,因为它们是继承的。
for (let i of iterable) { console.log(i); // logs 3, 5, 7 }
该循环迭代并记录iterable
作为可迭代对象定义的迭代值,这些是数组元素 3
, 5
, 7
,而不是任何对象的属性。
摘取自MDN