迭代器
Iterator
对象是符合迭代器协议的对象,它提供一个 next()
方法,该方法返回一个迭代器结果对象。所有内置迭代器都继承自 Iterator
类。Iterator
类提供一个[Symbol.iterator]()
方法,该方法返回迭代器对象本身,使迭代器也可迭代。它还提供了一些用于处理迭代器的辅助方法。
描述
以下是所有内置 JavaScript 迭代器
- 由
Array.prototype.values()
、Array.prototype.keys()
、Array.prototype.entries()
、Array.prototype[Symbol.iterator]()
、TypedArray.prototype.values()
、TypedArray.prototype.keys()
、TypedArray.prototype.entries()
、TypedArray.prototype[Symbol.iterator]()
和arguments[Symbol.iterator]()
返回的数组迭代器。 - 由
String.prototype[Symbol.iterator]()
返回的字符串迭代器。 - 由
Map.prototype.values()
、Map.prototype.keys()
、Map.prototype.entries()
和Map.prototype[Symbol.iterator]()
返回的映射迭代器。 - 由
Set.prototype.values()
、Set.prototype.keys()
、Set.prototype.entries()
和Set.prototype[Symbol.iterator]()
返回的集合迭代器。 - 由
RegExp.prototype[Symbol.matchAll]()
和String.prototype.matchAll()
返回的正则表达式字符串迭代器。 - 由生成器函数 返回的
Generator
对象。 - 由
[Symbol.iterator]()
方法(它是通过Intl.Segmenter.prototype.segment()
返回的Segments
对象的方法)返回的段迭代器。 - 由迭代器辅助方法(如
Iterator.prototype.filter()
和Iterator.prototype.map()
)返回的迭代器辅助。
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
类本身提供了一些用于处理迭代器的辅助方法。例如,您可能想要执行以下操作
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()
方法
const totalDeposit = nameToDeposit.values().reduce((a, b) => a + b);
这种方法效率更高,因为它只遍历一次迭代器,而不会记住任何中间值。迭代器助手方法对于处理无限迭代器是必需的
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.drop()
和Iterator.prototype.take()
结合在一起,有点类似于Array.prototype.slice()
。
在这些方法中,filter()
,flatMap()
,map()
,drop()
和take()
返回一个新的迭代器助手对象。迭代器助手也是一个Iterator
实例,使助手方法可链式调用。所有迭代器助手对象都继承自一个公共原型对象,该对象实现了迭代器协议
迭代器助手与底层迭代器共享相同的数据源,因此遍历迭代器助手也会导致底层迭代器被遍历。没有办法“派生”迭代器以允许它被多次遍历。
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()
方法。
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
循环中使用它们
const arrIterator = [1, 2, 3].values();
for (const value of arrIterator) {
console.log(value);
}
// Logs: 1, 2, 3
规范
规范 |
---|
ECMAScript 语言规范 # sec-%iteratorprototype%-object |
浏览器兼容性
BCD 表格仅在浏览器中加载