遍历器接口的原生实现和自定义实现

103 阅读2分钟

遍历器的概念

遍历器(iterator)的作用有三个:一是为各种数据结构提供统一的遍历接口,二是使数据结构的成员按照一定的顺序进行排列,三是为遍历命令 for...of 消费。

function makeIterator(arr) {
  let index = 0;
  return {
    next: () => {
      return index < arr.length ? { value: arr[index++], done: false } : { value: undefined, done: true };
    },
  };
}

const iterator = makeIterator(["a", "b"]);
console.log(iterator.next()); // { value: 'a', done: false }
console.log(iterator.next()); // { value: 'b', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

使用 makeIterator 方法创建遍历器对象后,就可以调用里面的 next 方法,它会返回每个成员的信息对象,其中 value 是成员的值,有就返回没有就返回 undefined,而 done 是表示是否完成遍历,true 表示遍历结束。

iterator 接口

一个数据结构有 iterator 接口,也就是拥有 Symbol.iterator 属性,就说该数据结构是可遍历的。

原生具有 iterator 接口的数据结构有 String,Array,Set,Map,TypedArray,NodeList,函数的 arguments 对象。没有 iterator 接口的数据结构可以通过增加 Symbol.iterator 属性的方式,来实现数据结构的可遍历。

class RangeIterator {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  [Symbol.iterator]() {
    return this;
  }
  next() {
    let index = this.start;
    if (index < this.end) {
      this.start++;
      return { value: index++, done: false };
    } else {
      return { value: undefined, done: true };
    }
  }
}

const range = new RangeIterator(0, 3);

const res = range[Symbol.iterator]();
console.log(res.next()); // { value: 0, done: false }
console.log(res.next()); // { value: 1, done: false }
console.log(res.next()); // { value: 2, done: false }
console.log(res.next()); // { value: undefined, done: true }

for (var value of range) {
  console.log(value); // 0, 1, 2
}

RangeIterator 实例对象增加 Symbol.iterator 属性后,就说明该对象是可遍历的。这时候可以调用 Symbol.iterator 属性提供的函数得到实例对象,再通过调用里面的 next() 方法获取值。通过 for...of 遍历 RangeIterator 实例对象可以直接在每次遍历的时候打印获取值。

除了自定义实现一个 iterator 接口,也可以借助其他数据结构原生提供的 iterator 接口

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

const ttt = iterable[Symbol.iterator]();
console.log(ttt.next()); // { value: 'a', done: false }
console.log(ttt.next()); // { value: 'b', done: false }
console.log(ttt.next()); // { value: 'c', done: false }
console.log(ttt.next()); // { value: undefined, done: false }

for (const item of iterable) {
  console.log(item); // a b c
}

类数组对象 iterable 借助了数组的 Symbol.iterator 属性来部署自身的 Symbol.iterator 属性,该对象可以通过使用 Symbol.iterator 属性的方法再加上 next() 来获取值,也可以直接通过 for..of 来遍历里面的数据

小结

本文首先介绍了拥有 iterator 接口的数据结构,再介绍了给数据结构增加 iterator 接口的两种方法,自定义创建和借助其他数据结构实现。最后总结了 iterator 的三个作用。

参考资料:es6.ruanyifeng.com/#docs/itera…