Iterator迭代器相关处理

189 阅读3分钟

Iterator是什么?

Iterator 是一种接口机制,为各种不同的数据结构提供统一的访问机制,使数据成员能够按照某种次序排列。并且ES6提供了一个新的遍历命令——for...of循环,Iterator接口主要供for...of使用。

JavaScript表示集合的数据结构主要是数组和对象,ES6又提供了Map和Set。

Iterator 的遍历过程如下:

  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器Iterator 本质上就是一个指针对象。
  2. 第一次调用指针对象的next()方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的next()方法,可以将指针指向数据结构的第二个成员。
  4. ...
  5. 持续调用next()方法,直到最后一个成员。

每次调用next方法就会返回一个对象,这个对象有两个属性,分别是done和value,done属性的值是一个Boolean值,标识着下面是否还有成员。如果下面还有成员则为true,如果没有就是false,value的值是该指针指向的值。

JavaScript中原生部署Iterator接口的数据结构都有哪些?

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的arguments对象
  • NodeList对象

实现遍历器

一个对象如果要具备可被for...of循环调用的接口,就必须在Symbol.iterator的属性上部署遍历器生成方法。当然该方法部署在原型上也可以。

class Iterator {
  constructor(assemble) {
    let self = this;
    self.assemble = assemble;
    self.index = 0;
  }
   //在`Symbol.iterator`的属性上部署遍历器生成方法,供for...of调用
  [Symbol.iterator]() {
    return this;
  }
    
  next() {
    let self = this,
      assemble = self.assemble,
      index = self.index;
    if (index > assemble.length - 1) {
      return {
        done: true,
        value: undefined,
      };
    } else {
      return {
        done: false,
        value: assemble[self.index++],
      };
    }
  }
}
let obj = {
  0: "a",
  1: "b",
  2: "c",
  length: 3,
};
for (const value of new Iterator(obj)) {
  console.log(value);//a,b,c
}

//如果直接使用下面
//for (const value of obj) {
//      console.log(value);
//}
//会报Uncaught TypeError: obj is not iterable

从上面代码我们可以看出来,Iterator对象需要有next()方法。

对于一个对象部署方法实际上就是执行下面代码

let iterator = {
  0: "a",
  1: "b",
  2: "c",
  length: 3,
  [Symbol.iterator]: function() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index > self.length - 1) {
          return {
            done: true,
            value: undefined,
          };
        } else {
          return {
            done: false,
            value: self[index++],
          };
        }
      },
    };
  },
};
for (const value of iterator) {
  console.log(value);//a,b,c
}

obj是一个对象集合,可以说是一个类数组对象。部署后iterator接口后可以使用for...of循环调用

还有一个方式是直接使用数组对象的遍历器方法

let iterator={
    0:"a",
    1:"b",
    2:"c",
    length:3,
    [Symbol.iterator]:Array.prototype[Symbol.iterator]
}

遍历器对象还有另外两个方法

  • return()

    如果for...of循环提前退出(通常是因为出错,或者break语句,或者continue语句),如果一个对象在完成遍历前需要清理或者释放资源,就可以部署return 方法,另外,return必须返回一个对象,这是Generator规则决定的

  • throw()

    很少用到,且听下回Generator章节

默认调用Iterator接口的其他场合

  1. 解构赋值
  2. 扩展运算符
  3. yield*
  4. for...of
  5. Array.from()
  6. Map(),Set(),WeekMap(),WeekSet();
  7. promise.all()
  8. promise.race()

与其他遍历语法的比较

  • for循环:缺点就是写法麻烦
  • forEach:无法跳出循环,break,return都无效
  • for...in它更见适合遍历对象,它以字符串作为键名,不仅会遍历当前对象上的键值,还会遍历原型上的键值,for...in循环遍历顺序不统一