参考文档

迭代协议并不是新的内置实现或语法,而是协议。这些协议可以被任何遵循约定的对象实现。

迭代协议包含:可迭代协议(iterable protocol)和迭代器协议(iterator protocol)。

可迭代协议

可迭代协议允许 JavaScript 对象定义或定制它们的行为,如 for…of 结构。一些内置类型同时是内置可迭代对象,包含默认的迭代行为,如 ArrayMapObject 则不是。

要成为可迭代对象则必须实现 @@iterator 方法,这意味着对象(或原型链中)必须有一个键为 @@iterator 属性,可通过常量 Symbol.iterator 访问该属性。

[Symbol.iterator]

一个无参数的函数,其返回值为一个符合迭代器协议的对象

当一个对象需要被迭代的时候(比如被置入一个 for…of 循环),首先,会不带参数调用它的 @@iterator 方法,然后使用此方法返回的迭代器获得要迭代的值

值得注意的是调用此无参数函数时,它将对可迭代对象的方法进行调用。因此,在函数内部,this 关键字可用于访问可迭代对象的属性,以决定迭代过程中提供什么。

此函数可以是普通函数,也可以是生成器函数,以便在调用时返回迭代器对象。在此生成器函数的内部,可以使用 yield 逐一提供条目

迭代器协议

迭代器协议定义了产生有限或无限个值的标准方式,所有值都被迭代完毕后,会返回一个默认返回值。

对象需要实现以下语义的 next() 方法,才能成为迭代器

next()

无参数或接受一个参数,并返回符合 IteratorResult 格式的对象。如果再使用迭代器内置的语言特征时得到一个非对象返回值(例如 falseundefined),将会抛出 TypeError (iterator.next() returned a non-object value)。

所有迭代器协议方法 next(), return(), throw() 都应返回实现 IteratorResult 接口的对象。它必须有属性 donevalue

done: 若迭代器能够生成序列中下一个值,返回 false,否则返回 true

value: 迭代器返回的任何 JavaScript 值。

实际上,二者都不是严格要求的;如果返回没有任何属性的对象,等价于 { done: false, value: undefined }

如果一个迭代器返回一个 done: true 的结果,则后续任何 next() 调用都应返回 done: true,尽管这在语言层面不是强制的。

next() 方法可以传递一个值,该值将成为相应 yield 表达式的值。

此外,迭代器实现了 return(value)throw(exception) 方法,这些方法在调用时告诉迭代器,调用者已经完成迭代。

异步迭代器和异步可迭代协议

异步迭代器和异步可迭代协议与迭代器和迭代器协议悠着非常相似的接口,只是调用迭代器方法的每个返回值都包装在一个 Promise 中。

和 [Symbol.iterator] 类似,异步可迭代协议需要实现 [Symbol.asyncIterator] 方法。

内置的可迭代对象

String, Array, TypedArray, Map, Set, and Segments 都是内置的可迭代对象,因为他们每个 prototype 都实现了 @@iterator 方法。

1
2
3
4
5
6
7
8
9
10
// 可以在浏览器中输入 console.log([][Symbol.iterator]()) 查看输出内容
console.log([][Symbol.iterator]());

Array Iterator {}
[[Prototype]]: Array Iterator ==> This is the prototype shared by all array iterators
next: ƒ next()
Symbol(Symbol.toStringTag): "Array Iterator"
[[Prototype]]: Object ==> This is the prototype shared by all built-in iterators
Symbol(Symbol.iterator): ƒ [Symbol.iterator]()
[[Prototype]]: Object ==> This is Object.prototype

接受可迭代对象的内置 API

有很多 API 接受可迭代对象,如

  • Map()
  • WeakMap()
  • Set()
  • WeakSet()
  • Promise.all()
  • Promise.allSettled()
  • Promise.race()
  • Promise.any()
  • Array.from()
1
2
3
4
5
6
7
8
9
10
// 验证示例代码
const myObj = {};

new WeakSet(
(function* () {
yield {};
yield myObj;
yield {};
})()
).has(myObj); // true