迭代器

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

描述

以下是所有内置 JavaScript 迭代器

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

  • 类似数组 的对象(如NodeList)从它们各自的方法 keys()values()entries()[Symbol.iterator]() 返回一个数组迭代器
  • 来自 Web API 的类似映射 的对象(如Headers)从它们各自的方法 keys()values()entries()[Symbol.iterator]() 返回它们自己的迭代器类型,如Headers 迭代器
  • 来自 Web API 的类似集合 的对象(如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.iterator]()

返回迭代器对象本身。这允许迭代器对象也成为可迭代的。

示例

将迭代器用作可迭代对象

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

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

规范

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

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅