迭代器及迭代器协议 - 拾起忽略的知识点

迭代器是咱们咱们平时接触不少的一个特性es6

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

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

①可迭代协议

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

当咱们执行如下代码时,能够明确知道什么值能够被循环获得异步

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

如何支持可迭代协议呢?
对象(或者它原型链上的某个对象)必须有一个名字是 Symbol.iterator 的属性async

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

② 迭代器协议

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

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

next方法:
提供一个对象的无参函数,返回的对象拥有两个属性:
done: boolean 不提供则默认是false
value: 用于迭代时获取的对象this

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

验证迭代对象中的next方法spa

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
复制代码
相关文章
相关标签/搜索