【前端】【javascript】es6中的遍历器接口Iterator

  很久没发文章啦-。-为了证实我还活着,我决定从笔记里面抓一篇还算不乱比较像文章的发出来。。。
  这些笔记是我在学es6的时候断断续续记录的,最近会一份一份整理陆陆续续发出来,顺便也本身再看一遍。我学习es6的时候主要是阅读的阮一峰大神的文章,有须要的能够搜索来阅读,很系统。
  
1.概念
   第一次看到这个概念是当年学C++的时候,STL库中的迭代器。在es6中,Iterator也差很少是这个意思。
  在es6中,能表示“集合”概念的数据类型大体有四种。  
   Array,Object,Map,Set
 
  既然是集合,那遍历即是一种基本需求。而Iterator就是为了提供一种统一的接口机制。任何的数据结构,只要部署了Iterator接口,即可以使用相似的方式完成遍历操做。
 
  固然,Iterator还有2个做用,它使数据结构的成员按某种次序排列,其次,es6有一种新的遍历方式,前面也说过,for...of,而Iterator的主要做用,就是支持此操做。
 
  Iteartor的遍历过程和C++语言同样
  1)建立一个指向数据结构起始位置的指针。(起始位置不是第一个成员的位置,起始位置使一个单独的标志位。)
  2)当调用next()方法,指针就向后移动一个位置,并返回当前位置上的成员,直到指针指向数据结构的结束位置为止。
 
  第二步中,js语言返回的的成员信息是两个,value和done,value不用介绍,done是一个表示遍历是否结束的布尔值。
 
 
2.部署接口
   上面咱们说到的部署接口,那js怎么部署接口呢。其实咱们以前已经说到过,在Symbol一节中,介绍了不少es6内置的Symbol值,这些就是接口。
  es6中有三类结构生来就具备Iterator接口:数组、类数组对象、Map和Set结构。
  
var arr = [1,2,3,4];
let iterator = arr[Symbol.iterator]();

console.log(iterator.next());  //{ value: 1, done: false }
console.log(iterator.next());  //{ value: 2, done: false }
console.log(iterator.next());  //{ value: 3, done: false }
console.log(iterator.next());  //{ value: 4, done: false }
console.log(iterator.next());  //{ value: undefined, done: true }
 
 
   至于对象没有布置iterator接口的缘由,不知道最近你们有没有看根据《你一辈子的故事》拍成的电影“降临",片中出现的外星语言是一门非线性的语言。而咱们说的数组,Map等结构中的成员都是有顺序的,即都是线性的结构,而对象,各成员并无一个肯定的顺序,因此遍历时先遍历谁后遍历谁并不肯定。因此,给一个对象部署iterator接口,其实就是对该对象作一种线性转换。若是你有这种须要,就须要手动给你的对象部署iterator接口咯~
  如:
  
let obj = {
    data: [ 'hello', 'world' ],
    [Symbol.iterator]() {
        const self = this;
        let index = 0;
        return {
            next() {
                if (index < self.data.length) {
                    return {
                        value: self.data[index++],
                        done: false
                    };
                } else {
                    return { value: undefined, done: true };
                }
            }
        };
    }
};

 

 
   能够看到,Symbol.iterator会返回一个对象,这就是一个遍历器对象,而做为遍历器对象,其必须具有的特征就是必须具有next()方法。
咱们还能够据此实现指针结构的数据结构。具体略~
 
  至于可使用Array.from转换成数组的类数组对象,部署iterator有一种很简单的方法,即直接使用数组的[Symbol.iterator]接口。
  
fakeArray.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

 

  固然,不知道大家看到next是否想到了es6的一个新玩意儿,即Generator函数。用Generator函数来实现Symbol.iterator接口,事半功倍。
  
var yieldIterator = {};
yieldIterator[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};

[...yieldIterator] // [1, 2, 3]
 
   注意,yield* 后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
  其次,其它调用到遍历器的操做还有解构赋值、扩展操做符、其它任何接受数组做为参数的场合,如:
  • for...of
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()(好比)
  • Promise.all()
  • Promise.race()
 
 
  一旦当你给你的结构部署了iterator接口,那么恭喜你,你可使用for...of来遍历你的结构了!
 
  遍历器对象除了必须布置next方法之外,还有2个可选方法。return()和throw()。当一个解构在遍历的时候异常提早退出(好比break,continue或者出错)的时候,就会调用return方法,其次,return方法必须返回一个对象。
至于throw方法,则是用于抛出错误,Generator.prototype.throw这里不展开讲了,感兴趣的能够搜索一下。
 
  for of循环有不少优势,好比不像for...in同样只遍历键名(甚至包括原型链上的键),并且不像foreach不能跳出循环。而且for...of为各类数据结构提供了一个统一的遍历方法。因此,尽可能使用它吧~