ES6 引入了 Map,Set 等,数据结构越来越多,需要一个统一的接口来处理不同的数据结构,所以引入了迭代器,提供了统一的访问方式:for...of 循环

迭代器 IteratorES6

  • 是一个对象,对象里面有 next() 方法
  • next() 方法返回的结果的格式必须是 { done: Boolean, value: any }
Iterator = {
  // ...
  next() {
    // ...
    return {
      value: any,
      done: Boolean
    }
  }
}

可迭代对象

是一个特殊的对象:

  • 这个特殊的对象要有一个方法:Symbol.iterator(){}(for…of 循环启动时,就会调用这个方法)
  • 方法的返回值是 迭代器(Iterator)(从此开始,for…of 仅作用于这个被返回的对象)
iterableObj = {
  // ...
  [Symbol.iterator]() {
    return { // return Iterator
      // ...
      next() {
        // ...
        return {
            value: any,
            done: Boolean
        }
      }
    }
  }
}

可迭代

只要满足上面可迭代对象的条件就是可迭代的,有以下作用:

  • 可以被 for...of 循环遍历
  • 可用于 展开语法
  • 解构赋值
  • yield*
  • Array.from()
  • Promise.all()
  • Promise.race()

内置的可迭代对象

  • String
  • Array
  • Map
  • Set
  • 函数的 arguments 对象
  • NodeList
  • TypedArray
  • FormData
  • URLSearchParams

此时可以通过 for...of 循环遍历元素(解决了 for...in 遍历对象的“属性”问题以及 Set,Map 等的遍历问题)

数据转换

用途

将一个普通对象改成可迭代对象:

let range = {
  from: 1,
  to: 5,
 
  [Symbol.iterator]() { // 在 for..of 循环开始时被调用一次
    return {
      current: this.from,
      last: this.to,
 
      next() { // 每次迭代时都会被调用,来获取下一个值
        if (this.current <= this.last) {
          return { done: false, value: this.current++ }
        } else {
          return { done: true }
        }
      }
    };
  }
}
 
for(let value of range) {
  console.log(value); // 1,然后 2,然后 3,然后 4,然后 5
}

异步可迭代对象

与上面类似,但是可以异步地每秒返回一个值:

let range = {
  from: 1,
  to: 5,
 
  [Symbol.asyncIterator]() { // (1)
    return {
      current: this.from,
      last: this.to,
 
      async next() { // (2)
 
        // 注意:我们可以在 async next 内部使用 "await"
        await new Promise(resolve => setTimeout(resolve, 1000)) // (3)
 
        if (this.current <= this.last) {
          return { done: false, value: this.current++ }
        } else {
          return { done: true }
        }
      }
    };
  }
};
 
(async () => {
 
  for await (let value of range) { // (4)
    console.log(value); // 1,2,3,4,5
  }
 
})()

(1)使用 Symbol.asyncIterator
(2)next() 返回一个 promise
(3)next() 可以不是 async 函数,但是里面使用 await 更方便
(4)使用 for await...of 进行迭代

Warning

需要常规的同步 iterator 的功能,无法与异步 iterator 一起使用
例如,spread 语法无法工作:alert( [...range] ); // Error, no Symbol.iterator

Iterator 和异步 iterator 之间差异

Iterator异步 iterator
提供 iterator 的对象方法Symbol.iteratorSymbol.asyncIterator
next() 返回值任意值Promise
要进行循环,使用for...offor await...of

NOTE

for...of 会寻找同步 Iterator,for await...of 会寻找异步 Iterator。可以在一个对象中同时添加同步和异步的 Iterator,拥有 2 种迭代方式,但是这样会比较奇怪

Warning

同步 Iterator 地 next 如果返回了一个 Promise,那么 for...of 将无限循环。因为 Promise 对象不是期望的格式,for…of 循环无法正确处理它,并且认为迭代尚未完成