Iterator

Baseline 广泛可用 *

此特性已非常成熟,可在多种设备和浏览器版本上使用。自 ⁨2016 年 9 月⁩以来,它已在各大浏览器中可用。

* 此特性的某些部分可能存在不同级别的支持。

一个 Iterator 对象是一个符合迭代器协议的对象,它通过提供一个 next() 方法来返回一个迭代器结果对象。所有内置迭代器都继承自 Iterator 类。Iterator 类提供了一个 [Symbol.iterator]() 方法,该方法返回迭代器对象本身,使得迭代器也成为可迭代对象。它还提供了一些用于处理迭代器的辅助方法。

描述

以下是所有内置的 JavaScript 迭代器:

Web API 也可能返回迭代器。有些重用了核心 JavaScript 迭代器,而另一些则定义了自己的迭代器。例如:

  • 类数组对象,例如 NodeList,从其各自的 keys()values()entries()[Symbol.iterator]() 方法返回一个 数组迭代器
  • 来自 Web API 的类 Map 对象,例如 Headers,从其各自的 keys()values()entries()[Symbol.iterator]() 方法返回其自己的迭代器类型,如 Headers 迭代器
  • 来自 Web API 的类 Set 对象,例如 FontFaceSet,从其各自的 keys()values()entries()[Symbol.iterator]() 方法返回其自己的迭代器类型,如 FontFaceSet 迭代器

注意: NodeIterator 和其他旧接口虽然名称如此,但并不符合迭代器协议可迭代协议

这些迭代器中的每一个都有一个独特的原型对象,它定义了特定迭代器使用的 next() 方法。例如,所有字符串迭代器对象都继承自一个隐藏对象 StringIteratorPrototype,它有一个 next() 方法,该方法按代码点迭代字符串。StringIteratorPrototype 还有一个 [Symbol.toStringTag] 属性,其初始值为字符串 "String Iterator"。此属性在 Object.prototype.toString() 中使用。类似地,其他迭代器原型也有自己的 [Symbol.toStringTag] 值,这些值与上面给出的名称相同。

所有这些原型对象都继承自 Iterator.prototype,它提供了一个 [Symbol.iterator]() 方法,该方法返回迭代器对象本身,使迭代器也成为可迭代对象

迭代器辅助方法

注意: 这些方法是迭代器辅助方法,而不是可迭代对象辅助方法,因为对象可迭代的唯一要求是存在 [Symbol.iterator]() 方法。没有共享的原型来安装这些方法。

Iterator 类本身提供了一些用于处理迭代器的辅助方法。例如,您可能会尝试以下操作:

js
const nameToDeposit = new Map([
  ["Anne", 1000],
  ["Bert", 1500],
  ["Carl", 2000],
]);

const totalDeposit = [...nameToDeposit.values()].reduce((a, b) => a + b);

这首先将 Map.prototype.values() 返回的迭代器转换为数组,然后使用 Array.prototype.reduce() 方法计算总和。但是,这既创建了一个中间数组,又对数组进行了两次迭代。相反,您可以直接使用迭代器本身的 reduce() 方法:

js
const totalDeposit = nameToDeposit.values().reduce((a, b) => a + b);

这种方法可能更有效,尤其是在内存方面,因为它只迭代迭代器一次,而不记忆任何中间值。迭代器辅助方法对于处理无限迭代器是必要的。

js
function* fibonacci() {
  let current = 1;
  let next = 1;
  while (true) {
    yield current;
    [current, next] = [next, current + next];
  }
}

const seq = fibonacci();
const firstThreeDigitTerm = seq.find((n) => n >= 100);

您无法将 seq 转换为数组,因为它是无限的。相反,您可以使用迭代器本身的 find() 方法,该方法只在必要时迭代 seq 以找到满足条件的第一个值。

您会发现许多迭代器方法与数组方法类似,例如:

迭代器方法 数组方法
Iterator.prototype.every() Array.prototype.every()
Iterator.prototype.filter() Array.prototype.filter()
Iterator.prototype.find() Array.prototype.find()
Iterator.prototype.flatMap() Array.prototype.flatMap()
Iterator.prototype.forEach() Array.prototype.forEach()
Iterator.prototype.map() Array.prototype.map()
Iterator.prototype.reduce() Array.prototype.reduce()
Iterator.prototype.some() Array.prototype.some()

Iterator.prototype.drop()Iterator.prototype.take() 结合起来有点类似于 Array.prototype.slice()

迭代器辅助对象

注意: 迭代器辅助对象迭代器辅助方法是两个不同的概念。迭代器辅助对象可以在运行时检测到,而“迭代器辅助方法”只是用于理解的一组方法的名称。迭代器辅助器可能指对象或方法,具体取决于上下文。

在迭代器辅助方法中,filter()flatMap()map()drop()take() 返回一个新的 迭代器辅助器 对象。迭代器辅助器也是一个 Iterator 实例,这使得这些辅助方法可以链式调用。所有迭代器辅助对象都继承自一个公共原型对象,该对象实现了迭代器协议:

next()

调用底层迭代器的 next() 方法,将辅助方法应用于结果,并返回结果。

return()

调用底层迭代器的 return() 方法,并返回结果。

迭代器辅助器与底层迭代器共享相同的数据源,因此迭代迭代器辅助器也会导致底层迭代器被迭代。没有办法“分叉”一个迭代器以使其能够多次迭代。

js
const it = [1, 2, 3].values();
const it2 = it.drop(0); // Essentially a copy
console.log(it.next().value); // 1
console.log(it2.next().value); // 2
console.log(it.next().value); // 3

真正的迭代器

有两种“迭代器”:符合迭代器协议的对象(最少只要求存在 next() 方法),以及继承自 Iterator 类并享有辅助方法的对象。它们并不相互包含——继承自 Iterator 的对象不会自动成为迭代器,因为 Iterator 类不定义 next() 方法。相反,对象需要自己定义一个 next() 方法。一个真正的迭代器是既符合迭代器协议又继承自 Iterator 的迭代器,并且大多数代码期望迭代器是真正的迭代器,并且可迭代对象返回真正的迭代器。要创建真正的迭代器,可以定义一个扩展 Iterator 的类,或使用 Iterator.from() 方法。

js
class MyIterator extends Iterator {
  next() {
    // …
  }
}

const myIterator = Iterator.from({
  next() {
    // …
  },
});

构造函数

Iterator()

旨在被创建迭代器的其他类扩展。如果单独构造,会抛出错误。

静态方法

Iterator.from()

从迭代器或可迭代对象创建一个新的 Iterator 对象。

实例属性

这些属性在 Iterator.prototype 上定义,并由所有 Iterator 实例共享。

Iterator.prototype.constructor

创建实例对象的构造函数。对于 Iterator 实例,初始值是 Iterator 构造函数。

Iterator.prototype[Symbol.toStringTag]

[Symbol.toStringTag] 属性的初始值为字符串 "Iterator"。此属性在 Object.prototype.toString() 中使用。

注意: 与大多数内置类上的 [Symbol.toStringTag] 不同,Iterator.prototype[Symbol.toStringTag] 是可写的,这是出于 Web 兼容性原因。

实例方法

Iterator.prototype.drop()

返回一个新的迭代器辅助对象,该对象跳过此迭代器开头的给定数量的元素。

Iterator.prototype.every()

测试迭代器产生的所有元素是否通过提供的函数实现的测试。

Iterator.prototype.filter()

返回一个新的迭代器辅助对象,该对象只生成迭代器中那些提供的回调函数返回 true 的元素。

Iterator.prototype.find()

返回迭代器产生的第一个满足提供的测试函数的元素。如果没有值满足测试函数,则返回 undefined

Iterator.prototype.flatMap()

返回一个新的迭代器辅助对象,该对象接受原始迭代器中的每个元素,通过映射函数处理它,并生成映射函数返回的元素(这些元素包含在另一个迭代器或可迭代对象中)。

Iterator.prototype.forEach()

对迭代器产生的每个元素执行一次提供的函数。

Iterator.prototype.map()

返回一个新的迭代器辅助对象,该对象生成迭代器的元素,每个元素都由映射函数转换。

Iterator.prototype.reduce()

对迭代器产生的每个元素执行一个用户提供的“reducer”回调函数,传入对前一个元素计算的返回值。运行 reducer 对所有元素的最终结果是一个单一值。

Iterator.prototype.some()

测试迭代器中至少有一个元素通过提供的函数实现的测试。它返回一个布尔值。

Iterator.prototype.take()

返回一个新的迭代器辅助对象,该对象生成此迭代器中给定数量的元素,然后终止。

Iterator.prototype.toArray()

创建一个新的 Array 实例,其中填充了从迭代器生成的元素。

Iterator.prototype[Symbol.dispose]()

如果存在,则调用 thisreturn() 方法。这实现了可处置协议,并允许在使用 usingawait using 时对其进行处置。

Iterator.prototype[Symbol.iterator]()

返回迭代器对象本身。这使得迭代器对象也可以是可迭代的。

示例

将迭代器用作可迭代对象

所有内置迭代器也都是可迭代的,因此您可以在 for...of 循环中使用它们:

js
const arrIterator = [1, 2, 3].values();
for (const value of arrIterator) {
  console.log(value);
}
// Logs: 1, 2, 3

规范

规范
ECMAScript® 2026 语言规范
# sec-%iteratorprototype%-object

浏览器兼容性

另见