迭代器是我们我们平时接触很多的一个特性

当我们遍历数组、字符串或者使用解构赋值、对象展开运算符时都会用到迭代器协议相关内容

1. 什么是可迭代协议和迭代器协议

① 可迭代协议

顾名思义,就是让对象支持被迭代的协议

当我们执行以下代码时,可以明确知道什么值可以被循环得到

let a = [1,2,3]
for(let i of a) {
 console.log(i) // 1,2,3
}

如何支持可迭代协议呢?

对象(或者它原型链上的某个对象)必须有一个名字是 Symbol.iterator 的属性

Symbol.iterator : 提供一个无参函数, 返回的对象实现迭代器协议

② 迭代器协议

迭代器协议定义了如何产生和返回迭代器函数的结果

实现迭代器协议的对象至少应该包含一个next方法

next方法:

提供一个对象的无参函数,返回的对象拥有两个属性: 

done: boolean 不提供则默认是false

value: 用于迭代时获取的对象,

2. 验证现有实现可迭代协议对象是否具备上述的属性

验证迭代对象中的next方法

let a = [1,2,3]
var aIt = a[Symbol.iterator]()
aIt.next()
//{value: 1, done: false}
aIt.next()
//{value: 2, done: false}
aIt.next()
//{value: 3, done: false}
aIt.next()
//{value: undefined, done: true}

next方法用于依次返回用于迭代的对象,咋一看,这不就是 generator的next方法吗

 

3. 如何对自定义对象实现迭代器协议

a. 通过扩展对象,实现协议定义的方法和对象

var c={a:1,1:2,c:4,b:5}
for(let i of c) {
 console.log(c)
}
//Uncaught TypeError: c is not iterable at <anonymous>:2:14

接下里我们按照上述协议内容将c对象扩展成支持for...of遍历, 首先实现可迭代协议 即为对象添加Symbol.iterator属性

c[Symbol.iterator] = function(){
  return {}
}

如何实现迭代器协议, 即在return中返回包含next方法的对象

c[Symbol.iterator] = function(){
  return {
    _i:0,
    next() {
       let keys = Object.keys(c)
		if(this._i<keys.length){
          console.log("调用iterator-next:"+i)
          return {value: keys[this._i++],done: false}
        }else {
         return {done: true, value:undefined}  
      }
    }
  }
}

测试调用

for(let i of c){console.log(i)}
//1 a c b

 

注: 以上实现中 next方法 this对象指向的是next返回对象  

 b. 利用generator对象

var c={a:1,1:2,c:4,b:5}
function* ci() {
   let keys = Object.keys(c),i=0
   while(i<keys.length){
    yield keys[i++]
  } 
}
c[Symbol.iterator] = ci

调用测试

for(let i of c){console.log(i)}
// 1 a c b

 

================

为什么可以使用generator实现
1.(MDN)调用一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的 迭代器 (iterator )对象。当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止,yield 后紧跟迭代器要返回的值。或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)
2. cig = new ci()// generator对象。 cig[Symbol.iterator]().next === cig.next

=================

 c. 在对象原型链上实现通用的迭代器协议

Object.prototype[Symbol.iterator] = function(){
    var i=0;
   return {next:() =>{
     var keys = Object.keys(this)
    if(i< keys.length){
        return {value: keys[i++],done:false}
    }else{ 
     return {done: true} 
 }
  },
}
}

测试

var a = {b:1}

for(let i of a){console.log(i)}
// b

for(let i in a){console.log(i)}
// b

3. 实现可迭代协议有什么作用 

①. es6中的for...of语法就是基于迭代协议的

let a = [1,2,3]
for(let i of a) {
  console.log(i)
}
//1
//2
//3

②.  对象展开符

var b = {a:1,1:2,c:4,b:5}
[...b]
// Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))

而对上面我们修改的对象C执行以下代码

[...c]
// ["1", "a", "c", "b"]

③. yield* 表达式

function* a(){
 yield* c
}
var ac = a()
ac.next()
//{value: "1", done: false}
ac.next()
//{value: "a", done: false}
ac.next()
//{value: "c", done: false}
ac.next()
//{value: "b", done: false}
ac.next()
//{value: undefined, done: true}

④. 解构赋值

借助对象C的console语句观察解构赋值的过程

[a1,b1] = c
// 调用iterator-next:0
// 调用iterator-next:1
// a1: 1  b1: a

  

以上就是迭代协议相关内容及运用

发现了Symbol.iterator属性的神奇之处,忍不住又看看了Symbol其他的数据, 发现又一个叫asyncIterator的属性

参考iterator, 那就是定义了一个异步可迭代对象了吧  ~_~

4. asyncIterator属性作用

 用于被for await...of 语句循环使用 (注: for await...of 可用于遍历同步或异步实现迭代器协议的对象)

①. 实现方式

同iterator,需要含义Symbol.asyncIterator属性, 提供返回一个无参函数,返回一个包含next方法的对象

②. 举个栗子

暂时可能没有想到什么必须合适的场景,那就无病呻吟一下吧,↓

先验证下for await...of

var a = [Promise.resove(1),Promise.resolve(2),Promise.resolve(3)]

for(let i of a){
 console.log(i)
}
// promise * 3

for await(let i of a) {
  console.log(i)
}
// 1,2,3

 

使用异步生成器构造一个异步可迭代对象

var asyncIterable = {
  [Symbol.asyncIterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return Promise.resolve({ value: this.i++, done: false });
        }

        return Promise.resolve({ done: true });
      }
    };
  }
};

for await (let num of asyncIterable) {
  console.log(num);
}
// 0,1,2

 

③,现状

目前还木有实现了asyncIterator内置属性的对象

 

5. Iterator对象

注: 非标准属性,只在FF下有实现,慎用

Iterator 函数返回一个对象,它实现了遗留的迭代协议,并且迭代了一个对象的可枚举属性。

var a = {a: 1,1: 2,c:3,b:4};

for (var [name, value] of Iterator(a)) {
  console.log(name, value); 
}
// 1,2 a,1  c,3  b,4