JavaScript中的迭代器

87 阅读3分钟

一、什么是迭代器

迭代器是一种设计模式,这种模式是为了解决集合等容器的遍历问题,使用者无需关注容器内部的实现。从实现上看,迭代器每一次得到的结果都会作为下一次迭代的值,这个通常使用指针维护,而不是单纯的重复。下面是js实现迭代器接口示例


const myContainer = {
  //数组作为容器,拥有length属性。
  store: ["apple", "banner", "pearl"],

  [Symbol.iterator]: function () {
    let index = 0;
    const self = this;

    return {
      next: function () {
        const result = {
          value: self.store[index],
          done: index >= self.store.length,
        };
        index++;
        return result;
      },
    };
  },
};

for (const item of myContainer) {
  console.log("loop:" + item);
}

二、ES6应用

ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费. 所有拥有Symbol.iterator属性的数据结构(值),都是可被迭代的,并且可以基于for of循环遍历。 常见的应用如下

1、结构后赋值

Set和Array的遍历基于迭代器应用示例如下:

const [a, b] = [1, 2];
//a: 1, b: 2

const [c, d] = new Set([3, 4]);
//c: 3, d: 4

Javascript引擎分别在左右两侧结构上调用next方法,逐个赋值,左侧数组的每个变量会对应被赋为右侧的值。

2、扩展运算符

ES6的扩展运算符也是通过Iterator接口实现,示例如下:

let args = ['username', 'gender', 'age'];
foo(...args);
//等价于foo('username', 'gender', 'age')

[Symbol.iterator]的对象都可以用扩展运算符展开,如开头的自定义示例

3、异常的处理
let obj = {
  username: '张三',
  gender: 1,

  [Symbol.iterator]: function (){
    let index = 0;
    let keys = ['username', 'gender'];
    let _this = this;
    
    return {
      next(){
        return index < keys.length ?
        		{value: _this[keys[index++]], done: false} :
        		{value: undefined, done: true}
      },
      return(){
        ...  //结束循环前可以在这里执行某些操作,如关闭文件系统等
        return {done: true}
      },
      throw(){
        ...  //抛出异常时可以在这里执行某些操作
        return {done: true}
      }
    }
  }
}

for(let val of s){
  console.log(val);
  break;   //该语句会触发遍历器对象的return方法
}

for(let val of s){
  console.log(val);
  throw new Error(); //该语句会触发遍历器对象的throw方法
}

三、迭代器优点和缺点

优点
  • 为集合和非集合类型提供了统一的取值策略,无需修改原有代码。
  • 调用的时候非常的方便,外界不用关心迭代器内部的实现,跟迭代器的交互也仅仅是一次初始化调用
缺点
  • 除非遍历到最后,否则无法获取迭代器长度
  • 迭代只能取到下一个值,不能回到开始位置,同一个迭代器被多个循环使用,只有一个循环能够获取到值
  • 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

四、总结

迭代器是ES6最为重要和基础的概念之一,很多新的ES6的语法都在它的基础上实现。 他是一种线性输出“集合”属性值的机制,方便了扩展和使用。另外迭代器的更多能力应该和生成器一起使用才能够体现出来,我们将在下一篇探讨迭代器与生成器的配合。