for...of

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

试一试

语法

js
for (variable of iterable)
  statement
变量

在每次迭代中从序列中接收一个值。可以是使用constletvar声明,也可以是赋值目标(例如,先前声明的变量、对象属性或解构赋值模式)。使用var声明的变量不是循环的局部变量,即它们与for...of循环所在的范围相同。

可迭代对象

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

语句

在每次迭代中执行的语句。可以引用variable。可以使用块语句来执行多个语句。

描述

for...of循环按顺序逐个操作来自可迭代对象的值。循环对值的每次操作称为迭代,并且据说循环迭代可迭代对象。每次迭代都执行可能引用当前序列值的语句。

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

当迭代器完成时(next()结果是一个具有done: true的对象),for...of循环退出。与其他循环语句一样,您可以在statement中使用控制流语句

  • break停止statement执行并转到循环后的第一个语句。
  • continue停止statement执行并转到循环的下一轮迭代。

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

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

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

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

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

可以使用解构来分配多个局部变量,或者使用属性访问器(如for (x.y of iterable))将值分配给对象属性。

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

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

示例

迭代数组

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

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

迭代字符串

字符串按Unicode代码点迭代

js
const iterable = "boo";

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

迭代TypedArray

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

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

迭代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

迭代Set

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

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

迭代arguments对象

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

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

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

迭代NodeList

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

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

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

迭代具有返回自定义迭代器的[Symbol.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

迭代具有[Symbol.iterator]()生成器方法的对象

js
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)中使用。

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

迭代生成器

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

const generator = source();

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

提前退出

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

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

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的区别

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

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

以下示例显示了当for...of循环和for...in循环与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

for...in 循环仅记录 iterable 对象的可枚举属性。它不会记录数组的元素 357"hello",因为这些不是属性——它们是。它记录了数组的索引以及 arrCustomobjCustom,这些是实际的属性。如果您不确定为什么迭代这些属性,则可以更全面地了解数组迭代和 for...in 的工作原理。

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

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

规范

规范
ECMAScript 语言规范
# sec-for-in-and-for-of-statements

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅