for...of

for...of 语句执行一个循环,该循环对源自 可迭代对象 的一系列值进行操作。可迭代对象包括内置实例,例如 ArrayStringTypedArrayMapSetNodeList(和其他 DOM 集合),以及 arguments 对象、生成器函数 生成的 generators 和用户定义的可迭代对象。

¥The for...of statement executes a loop that operates on a sequence of values sourced from an iterable object. Iterable objects include instances of built-ins such as Array, String, TypedArray, Map, Set, NodeList (and other DOM collections), as well as the arguments object, generators produced by generator functions, and user-defined iterables.

Try it

语法

¥Syntax

js
for (variable of iterable)
  statement
variable

每次迭代时从序列接收一个值。可以是带有 constletvar 的声明,也可以是 assignment 目标(例如先前声明的变量、对象属性或 解构赋值模式)。用 var 声明的变量不是循环的本地变量,即它们与 for...of 循环位于同一范围内。

iterable

一个可迭代的对象。循环运行的值序列的来源。

statement

每次迭代时执行的语句。可参考 variable。你可以使用 块语句 来执行多个语句。

描述

¥Description

for...of 循环对源自可迭代对象的值按顺序进行一一操作。循环对值的每次操作称为迭代,并且循环被称为对可迭代对象进行迭代。每次迭代都会执行可能引用当前序列值的语句。

¥A for...of loop operates on the values sourced from an iterable one by one in sequential order. Each operation of the loop on a value is called an iteration, and the loop is said to iterate over the iterable. Each iteration executes statements that may refer to the current sequence value.

for...of 循环迭代可迭代对象时,它首先调用可迭代对象的 [@@iterator]() 方法,该方法返回 iterator,然后重复调用结果迭代器的 next() 方法以生成要分配给 variable 的值序列。

¥When a for...of loop iterates over an iterable, it first calls the iterable's [@@iterator]() method, which returns an iterator, and then repeatedly calls the resulting iterator's next() method to produce the sequence of values to be assigned to variable.

当迭代器完成时,for...of 循环退出(迭代器的 next() 方法返回包含 done: true 的对象)。你还可以使用控制流语句来更改正常的控制流。break 退出循环并转到循环体之后的第一条语句,而 continue 则跳过当前迭代的其余语句并继续下一次迭代。

¥A for...of loop exits when the iterator has completed (the iterator's next() method returns an object containing done: true). You may also use control flow statements to change the normal control flow. break exits the loop and goes to the first statement after the loop body, while continue skips the rest of the statements of the current iteration and proceeds to the next iteration.

如果 for...of 循环提前退出(例如遇到 break 语句或抛出错误),则调用迭代器的 return() 方法来执行任何清理。

¥If the for...of loop exited early (e.g. a break statement is encountered or an error is thrown), the return() method of the iterator is called to perform any cleanup.

for...ofvariable 部分接受 = 运算符之前的任何内容。你可以使用 const 来声明变量,只要它不在循环体内重新分配即可(它可以在迭代之间更改,因为它们是两个单独的变量)。否则,你可以使用 let

¥The variable part of for...of accepts anything that can come before the = operator. You can use const to declare the variable as long as it's not reassigned within the loop body (it can change between iterations, because those are two separate variables). Otherwise, you can use let.

js
const iterable = [10, 20, 30];

for (let value of iterable) {
  value += 1;
  console.log(value);
}
// 11
// 21
// 31

注意:每次迭代都会创建一个新变量。在循环体内重新分配变量不会影响可迭代对象(在本例中为数组)中的原始值。

¥Note: Each iteration creates a new variable. Reassigning the variable inside the loop body does not affect the original value in the iterable (an array, in this case).

你可以使用 destructuring 分配多个局部变量,或使用像 for (x.y of iterable) 这样的属性访问器将值分配给对象属性。

¥You can use destructuring to assign multiple local variables, or use a property accessor like for (x.y of iterable) to assign the value to an object property.

但是,有一条特殊规则禁止使用 async 作为变量名。这是无效的语法:

¥However, a special rule forbids using async as the variable name. This is invalid syntax:

js
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 循环。

¥This is to avoid syntax ambiguity with the valid code for (async of => {};;), which is a for loop.

示例

¥Examples

迭代数组

¥Iterating over an Array

js
const iterable = [10, 20, 30];

for (const value of iterable) {
  console.log(value);
}
// 10
// 20
// 30

迭代字符串

¥Iterating over a string

琴弦为 按 Unicode 代码点迭代

¥Strings are iterated by Unicode code points.

js
const iterable = "boo";

for (const value of iterable) {
  console.log(value);
}
// "b"
// "o"
// "o"

迭代 TypedArray

¥Iterating over a TypedArray

js
const iterable = new Uint8Array([0x00, 0xff]);

for (const value of iterable) {
  console.log(value);
}
// 0
// 255

迭代 Map

¥Iterating over a Map

js
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

迭代集合

¥Iterating over a Set

js
const iterable = new Set([1, 1, 2, 2, 3, 3]);

for (const value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

迭代参数对象

¥Iterating over the arguments object

你可以迭代 arguments 对象来检查传递给函数的所有参数。

¥You can iterate over the arguments object to examine all parameters passed into a function.

js
function foo() {
  for (const value of arguments) {
    console.log(value);
  }
}

foo(1, 2, 3);
// 1
// 2
// 3

迭代 NodeList

¥Iterating over a NodeList

以下示例通过迭代 NodeList DOM 集合,将 read 类添加到作为 <article> 元素的直接后代的段落。

¥The following example adds a read class to paragraphs that are direct descendants of the <article> element by iterating over a NodeList DOM collection.

js
const articleParagraphs = document.querySelectorAll("article > p");
for (const paragraph of articleParagraphs) {
  paragraph.classList.add("read");
}

迭代用户定义的可迭代对象

¥Iterating over a user-defined iterable

使用返回自定义迭代器的 @@iterator 方法迭代对象:

¥Iterating over an object with an @@iterator method that returns a custom iterator:

js
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

使用 @@iterator 生成器方法迭代对象:

¥Iterating over an object with an @@iterator generator method:

js
const iterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  },
};

for (const value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

可迭代迭代器(具有返回 this[@@iterator]() 方法的迭代器)是一种相当常见的技术,可以使迭代器在需要可迭代的语法(例如 for...of)中使用。

¥Iterable iterators (iterators with a [@@iterator]() method that returns this) are a fairly common technique to make iterators usable in syntaxes expecting iterables, such as for...of.

js
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

迭代生成器

¥Iterating over a generator

js
function* source() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = source();

for (const value of generator) {
  console.log(value);
}
// 1
// 2
// 3

提早退出

¥Early exiting

在第一个循环中执行 break 语句会导致其提前退出。迭代器尚未完成,因此第二个循环将从第一个循环停止的地方继续。

¥Execution of the break statement in the first loop causes it to exit early. The iterator is not finished yet, so the second loop will continue from where the first one stopped at.

js
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() 方法,这使得生成器函数在循环退出时提前返回。这使得生成器无法在循环之间重用。

¥Generators implement the return() method, which causes the generator function to early return when the loop exits. This makes generators not reusable between loops.

js
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 之间的区别

¥Difference between for...of and for...in

for...infor...of 语句都会迭代某些内容。它们之间的主要区别在于它们迭代的内容。

¥Both for...in and for...of statements iterate over something. The main difference between them is in what they iterate over.

for...in 语句迭代对象的 可枚举字符串属性,而 for...of 语句迭代 可迭代对象 定义要迭代的值。

¥The for...in statement iterates over the enumerable string properties of an object, while the for...of statement iterates over values that the iterable object defines to be iterated over.

以下示例显示了 for...of 循环和 for...in 循环与 Array 一起使用时的区别。

¥The following example shows the difference between a for...of loop and a for...in loop when used with an Array.

js
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 继承属性 objCustomarrCustom,因为它的 原型链 中同时包含 Object.prototypeArray.prototype

¥The object iterable inherits the properties objCustom and arrCustom because it contains both Object.prototype and Array.prototype in its prototype chain.

for...in 循环仅记录 iterable 对象的 可枚举属性。它不会记录数组元素 357"hello",因为这些不是属性 - 它们是值。它记录数组索引以及 arrCustomobjCustom,它们是实际属性。如果你不确定为什么要迭代这些属性,这里有关于 数组迭代和 for...in 如何工作的更全面的解释。

¥The for...in loop logs only enumerable properties of the iterable object. It doesn't log array elements 3, 5, 7 or "hello" because those are not properties — they are values. It logs array indexes as well as arrCustom and objCustom, which are actual properties. If you're not sure why these properties are iterated over, there's a more thorough explanation of how array iteration and for...in work.

第二个循环与第一个循环类似,但它使用 Object.hasOwn() 来检查找到的可枚举属性是否是对象自己的,即不是继承的。如果是,则记录该属性。属性 012foo 会被记录,因为它们是自己的属性。属性 arrCustomobjCustom 不会被记录,因为它们是继承的。

¥The second loop is similar to the first one, but it uses Object.hasOwn() to check if the found enumerable property is the object's own, i.e. not inherited. If it is, the property is logged. Properties 0, 1, 2 and foo are logged because they are own properties. Properties arrCustom and objCustom are not logged because they are inherited.

for...of 循环迭代并记录 iterable 作为数组(即 iterable)定义要迭代的值。显示了对象的元素 357,但没有显示对象的任何属性。

¥The for...of loop iterates and logs values that iterable, as an array (which is iterable), defines to be iterated over. The object's elements 3, 5, 7 are shown, but none of the object's properties are.

规范

Specification
ECMAScript Language Specification
# sec-for-in-and-for-of-statements

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看