for...of
for...of
语句执行一个循环,该循环操作来自可迭代对象的一系列值。可迭代对象包括内置对象的实例,例如Array
、String
、TypedArray
、Map
、Set
、NodeList
(以及其他DOM集合),以及arguments
对象、由生成器函数生成的生成器,以及用户定义的可迭代对象。
试一试
语法
描述
for...of
循环按顺序逐个操作来自可迭代对象的值。循环对值的每次操作称为迭代,并且据说循环迭代可迭代对象。每次迭代都执行可能引用当前序列值的语句。
当for...of
循环迭代可迭代对象时,它首先调用可迭代对象的[Symbol.iterator]()
方法,该方法返回一个迭代器,然后重复调用生成的迭代器的next()
方法以生成要分配给variable
的值序列。
当迭代器完成时(next()
结果是一个具有done: true
的对象),for...of
循环退出。与其他循环语句一样,您可以在statement
中使用控制流语句
如果for...of
循环提前退出(例如,遇到break
语句或抛出错误),则调用迭代器的return()
方法以执行任何清理工作。
for...of
的variable
部分接受可以在=
运算符之前出现的任何内容。只要在循环体中不重新分配变量,就可以使用const
来声明变量(它可以在迭代之间更改,因为它们是两个单独的变量)。否则,可以使用let
。
const iterable = [10, 20, 30];
for (let value of iterable) {
value += 1;
console.log(value);
}
// 11
// 21
// 31
注意:每次迭代都会创建一个新变量。在循环体内部重新分配变量不会影响可迭代对象(在这种情况下为数组)中的原始值。
可以使用解构来分配多个局部变量,或者使用属性访问器(如for (x.y of iterable)
)将值分配给对象属性。
但是,特殊规则禁止使用async
作为变量名。这是无效的语法
let async;
for (async of [1, 2, 3]); // SyntaxError: The left-hand side of a for-of loop may not be 'async'.
这样做是为了避免与有效代码for (async of => {};;)
产生语法歧义,后者是一个for
循环。
示例
迭代数组
const iterable = [10, 20, 30];
for (const value of iterable) {
console.log(value);
}
// 10
// 20
// 30
迭代字符串
字符串按Unicode代码点迭代。
const iterable = "boo";
for (const value of iterable) {
console.log(value);
}
// "b"
// "o"
// "o"
迭代TypedArray
const iterable = new Uint8Array([0x00, 0xff]);
for (const value of iterable) {
console.log(value);
}
// 0
// 255
迭代Map
const iterable = new Map([
["a", 1],
["b", 2],
["c", 3],
]);
for (const entry of iterable) {
console.log(entry);
}
// ['a', 1]
// ['b', 2]
// ['c', 3]
for (const [key, value] of iterable) {
console.log(value);
}
// 1
// 2
// 3
迭代Set
const iterable = new Set([1, 1, 2, 2, 3, 3]);
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
迭代arguments对象
可以迭代arguments
对象以检查传递给函数的所有参数。
function foo() {
for (const value of arguments) {
console.log(value);
}
}
foo(1, 2, 3);
// 1
// 2
// 3
迭代NodeList
迭代用户定义的可迭代对象
迭代具有返回自定义迭代器的[Symbol.iterator]()
方法的对象
const iterable = {
[Symbol.iterator]() {
let i = 1;
return {
next() {
if (i <= 3) {
return { value: i++, done: false };
}
return { value: undefined, done: true };
},
};
},
};
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
迭代具有[Symbol.iterator]()
生成器方法的对象
const iterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
};
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
可迭代迭代器(具有返回this
的[Symbol.iterator]()
方法的迭代器)是一种相当常见的技术,用于使迭代器可在期望可迭代对象的语法(例如for...of
)中使用。
let i = 1;
const iterator = {
next() {
if (i <= 3) {
return { value: i++, done: false };
}
return { value: undefined, done: true };
},
[Symbol.iterator]() {
return this;
},
};
for (const value of iterator) {
console.log(value);
}
// 1
// 2
// 3
迭代生成器
function* source() {
yield 1;
yield 2;
yield 3;
}
const generator = source();
for (const value of generator) {
console.log(value);
}
// 1
// 2
// 3
提前退出
第一个循环中break
语句的执行导致它提前退出。迭代器尚未完成,因此第二个循环将从第一个循环停止的位置继续。
const source = [1, 2, 3];
const iterator = source[Symbol.iterator]();
for (const value of iterator) {
console.log(value);
if (value === 1) {
break;
}
console.log("This string will not be logged.");
}
// 1
// Another loop using the same iterator
// picks up where the last loop left off.
for (const value of iterator) {
console.log(value);
}
// 2
// 3
// The iterator is used up.
// This loop will execute no iterations.
for (const value of iterator) {
console.log(value);
}
// [No output]
生成器实现了return()
方法,当循环退出时,该方法会导致生成器函数提前返回。这使得生成器在循环之间不可重用。
function* source() {
yield 1;
yield 2;
yield 3;
}
const generator = source();
for (const value of generator) {
console.log(value);
if (value === 1) {
break;
}
console.log("This string will not be logged.");
}
// 1
// The generator is used up.
// This loop will execute no iterations.
for (const value of generator) {
console.log(value);
}
// [No output]
for...of和for...in的区别
for...in
和for...of
语句都迭代某些内容。它们之间主要的区别在于迭代的内容。
for...in
语句迭代对象的可枚举字符串属性,而for...of
语句迭代可迭代对象定义要迭代的值。
以下示例显示了当for...of
循环和for...in
循环与Array
一起使用时的区别。
Object.prototype.objCustom = function () {};
Array.prototype.arrCustom = function () {};
const iterable = [3, 5, 7];
iterable.foo = "hello";
for (const i in iterable) {
console.log(i);
}
// "0", "1", "2", "foo", "arrCustom", "objCustom"
for (const i in iterable) {
if (Object.hasOwn(iterable, i)) {
console.log(i);
}
}
// "0" "1" "2" "foo"
for (const i of iterable) {
console.log(i);
}
// 3 5 7
对象 iterable
继承了属性 objCustom
和 arrCustom
,因为它在它的原型链中同时包含了 Object.prototype
和 Array.prototype
。
for...in
循环仅记录 iterable
对象的可枚举属性。它不会记录数组的元素 3
、5
、7
或 "hello"
,因为这些不是属性——它们是值。它记录了数组的索引以及 arrCustom
和 objCustom
,这些是实际的属性。如果您不确定为什么迭代这些属性,则可以更全面地了解数组迭代和 for...in
的工作原理。
第二个循环类似于第一个循环,但它使用Object.hasOwn()
检查找到的可枚举属性是否是对象自身拥有的,即不是继承的。如果是,则记录该属性。属性 0
、1
、2
和 foo
被记录,因为它们是自身属性。属性 arrCustom
和 objCustom
未被记录,因为它们是继承的。
for...of
循环迭代并记录 iterable
(作为一个数组,它是可迭代的)定义要迭代的值。显示了对象的元素 3
、5
、7
,但没有显示对象的任何属性。
规范
规范 |
---|
ECMAScript 语言规范 # sec-for-in-and-for-of-statements |
浏览器兼容性
BCD 表格仅在浏览器中加载