既严肃认真,又生动活泼

ES6 的遍历器接口 Iterator

一、概念

遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6有一种新的遍历方式,for...of,而Iterator的主要作用,就是支持此操作。

 

二、是否具备遍历器(Iterator)接口

在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被for...of循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了Symbol.iterator属性(详见下文),另外一些数据结构没有。

凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。

在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。它们三者不需要我们手动部署Symbol.iterator属性就可以使用for...of遍历功能。而普通对象使用for...of遍历就会报错,因为它没有部署该接口。

 

三、部署接口

如果你需要使原本不具备for...of遍历功能的集合具备该功能,就需要手动为它部署。ES6有许多内置的Symbol值,这些就是接口。

下面我们利用迭代器中的原理来给对象生成一个迭代器,实现让对象可以使用for...of

 

var person={
          name: 'zz',
           age: 18
       }
//给person对象添加一个iterator接口 person[Symbol.iterator] = function(){
//使用object.keys()方法把j对象中的k值读取出来存在数组当中 var arr = Object.keys(person); var i = 0; return { //ES6中next()迭代方法,自动迭代 next(){ if(i < arr.length){ //如果done为false,继续迭代 return { //返回迭代的最后结果,如果是一个对象,那么for of的时候需要用解构 value: { k : arr[i], val: person[arr[i++]] }, done: false } }else{ //如果done为true,继续迭代 return { value: null, done : true } } } } }
//解构获取返回得到的对象,输出k值,val值 for(var {key,val} of person){ console.log(key,val); }

 

 

我们可以看到:

1、通过为person对象部署Symbol.iterator接口,就使它实现了for...of 功能。

2、Symbol.iterator中返回一个对象,该对象包含一个next() 方法,定义了遍历功能。

3、next方法中的value和done,value是遍历过程中返回的键值对信息,done是一个表示遍历是否结束的布尔值。

 

补充:

1、遍历器对象除了具有next方法,还可以具有return方法和throw方法。如果你自己写遍历器对象生成函数,那么next方法是必须部署的,return方法和throw方法是否部署是可选的。

  当一个解构在遍历的时候异常提前退出(比如break,continue或者出错)的时候,就会调用return方法,其次,return方法必须返回一个对象。

  至于throw方法,则是用于抛出错误,Generator.prototype.throw这里不展开讲了,感兴趣的可以搜索一下。

 

2、用ES6新功能Generator函数来实现Symbol.iterator接口,事半功倍。

var yieldIterator = {};
yieldIterator[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};

[...yieldIterator] // [1, 2, 3]

注意,yield* 后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

对Generator不了解的可以戳 ES6 Generator的语法 ,这里不多说。

 

3、至于可以使用Array.from转换成数组的类数组对象,部署iterator有一种很简单的方法,即直接使用数组的[Symbol.iterator]接口。

fakeArray.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

 

四、默认调用Iterator接口的场景

1、解构赋值

2、扩展运算符(...)

3、上文提到的 yield*

4、由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,都默认调用,如

  for...of

  Array.from()

  Map(), Set(), WeakMap(), WeakSet()

  Promise.all()

  Promise.race()

5、字符串是一个类似数组的对象,原生也具有Iterator接口

 

posted @ 2018-04-23 17:45  大宝章  阅读(896)  评论(0编辑  收藏  举报