:::danger 通过递增索引来遍历有序集合的缺点是什么?

  1. 迭代之前需要事先知道如何使用数据结构
  2. 遍历顺序并不是数据结构固有的。 :::

迭代器模式

:::danger 可迭代对象有哪些特点? :::

迭 代器(iterator)是按需创建的一次性对象。每个迭代器都会关联一个可迭代对象,而迭代器会暴露迭代 其关联可迭代对象的 API。迭代器无须了解与其关联的可迭代对象的结构,只需要知道如何取得连续的值。

可迭代协议

实现 Iterable 接口(可迭代协议)要求同时具备两种能力:支持迭代的自我识别能力和创建实现 Iterator 接口的对象的能力。在 ECMAScript 中,这意味着必须暴露一个属性作为“默认迭代器”,而 且这个属性必须使用特殊的 Symbol.iterator 作为键。

  1. var arr = [];
  2. arr.__proto__[Symbol.iterator] // ƒ values() { [native code] }

很多内置类型都实现了 Iterable 接口:

  1. String
  2. Number
  3. Map
  4. Set
  5. arguments
  6. NodeList

可以接收迭代器的原生对象有:

  • for-of 循环
  • 数组解构
  • 扩展操作符
  • Array.from()
  • 创建集合
  • 创建映射
  • Promise.all()接收由期约组成的可迭代对象
  • Promise.race()接收由期约组成的可迭代对象
  • yield*操作符,在生成器中使用

可选的 return()方法用于指定在迭代器提前关闭时执行的逻辑。执行迭代的结构在想让迭代器知 道它不想遍历到可迭代对象耗尽时,就可以“关闭”迭代器。
内置语言结构在发现还有更多值可以迭代,但不会消费(使用)这些值时,会自动调用 return()方法。

  1. class IteratorMock {
  2. constructor() {
  3. this.value = 0;
  4. }
  5. [Symbol.iterator](){
  6. return {
  7. next:() => {
  8. const currentValue = this.value +=1
  9. return {done:currentValue > 10, value:currentValue}
  10. },
  11. return(){
  12. console.log('end!!!')
  13. return {done:true}
  14. }
  15. }
  16. }
  17. }
  18. const arr = new IteratorMock()
  19. for (const arrayKey of arr) {
  20. console.log(arrayKey) // 输出 1 到 10
  21. }
  22. const arr = new IteratorMock()
  23. let [a, b] = arr; // end!!

如果迭代器没有关闭,则还可以继续从上次离开的地方继续迭代。

生成器

箭头函数不能用作生成器函数
调用生成器函数会产生一个生成器对象。生成器对象一开始处于暂停执行(suspended)的状态。与 迭代器相似,生成器对象也实现了 Iterator 接口,因此具有 next()方法。调用这个方法会让生成器 开始或恢复执行。
yield 关键字可以让生成器停止和开始执行,也是生成器最有用的地方。生成器函数在遇到 yield 关键字之前会正常执行。遇到这个关键字后,执行会停止,函数作用域的状态会被保留。

使用 yield 实现输入和输出

除了可以作为函数的中间返回语句使用,yield 关键字还可以作为函数的中间参数使用。上一次让 生成器函数暂停的 yield 关键字会接收到传给 next()方法的第一个值。

  1. function* gen(){
  2. var result = yield fetch('https://jsonplaceholder.typicode.com/todos/1');
  3. var result2 = yield fetch('https://jsonplaceholder.typicode.com/todos/2');
  4. console.log(result.title + ';' + result2.title);
  5. }
  6. var g = gen()
  7. var result = g.next()
  8. result.value.then(res => res.json()).then(res => g.next(res).value)
  9. .then(res => res.json()).then(res => g.next(res))

yield *

可以使用星号增强 yield 的行为,让它能够迭代一个可迭代对象,从而一次产出一个值:

  1. function* generatorFn() { yield* [1, 2, 3]; }