Iterator

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

描述

以下都是内置的 JavaScript 迭代器:

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

所有这些原型对象都继承自 Iterator.prototype,它提供了一个返回迭代器对象本身的 @@iterator 方法,这使迭代器也变得可迭代

迭代器辅助方法

备注: 这些方法是迭代器辅助方法,而不是可迭代对象辅助方法,因为可迭代对象的唯一要求就是具有 @@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); // 本质上是一个副本
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[@@toStringTag]

@@toStringTag 属性的初始值是字符串 "Iterator"。该属性在 Object.prototype.toString() 中使用。

备注: 与大多数内置类的 @@toStringTag 不同,出于 web 兼容性原因,Iterator.prototype[@@toStringTag] 是可写的。

实例方法

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[@@iterator]()

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

示例

使用迭代器作为可迭代对象

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

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

规范

Specification
ECMAScript Language Specification
# sec-%iteratorprototype%-object

浏览器兼容性

BCD tables only load in the browser

参见