JavaScript 迭代器以及它的使用场景

芝麻凛 2021年11月20日 164次浏览

JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。 更具体地说,迭代器是通过使用 next() 方法实现 Iterator protocol的任何一个对象,该方法返回具有两个属性的对象: value,这是序列中的 next 值;和 done ,如果已经迭代到序列中的最后一个值,则它为 true 。如果 valuedone 一起存在,则它是迭代器的返回值。

一旦创建,迭代器对象可以通过重复调用 next() 显式地迭代。 迭代一个迭代器被称为消耗了这个迭代器,因为它通常只能执行一次。 在产生终止值之后,对next() 的额外调用应该继续返回 {done:true}

Javascript中最常见的迭代器是 Array 迭代器,它只是按顺序返回关联数组中的每个值。 虽然很容易想象所有迭代器都可以表示为数组,但事实并非如此。 数组必须完整分配,但迭代器仅在必要时使用,因此可以表示无限大小的序列,例如0和无穷大之间的整数范围。

在项目中实际使用的简单的迭代器可能如下示例:

class LinkedList {
  constructor(data) {
    this.data = data;
  }

  firstItem() {
    return this.data.find(i => i.head);
  }

  findById(id) {
    return this.data.find(i => i.id === id);
  }

  [Symbol.iterator]() {
    let item = { next: this.firstItem().id };
    return {
      next: () => {
        item = this.findById(item.next);
        if (item) {
          return { value: item.value, done: false };
        }
        return { value: undefined, done: true };
      },
    };
  }
}

const myList = new LinkedList([
  { id: 'a10', value: 'First', next: 'a13', head: true },
  { id: 'a11', value: 'Last', next: null, head: false },
  { id: 'a12', value: 'Third', next: 'a11', head: false },
  { id: 'a13', value: 'Second', next: 'a12', head: false },
]);

for (let item of myList) {
  console.log(item); // 'First', 'Second', 'Third', 'Last'
}

在上面的例子中,我们实现了一个 LinkedList 数据结构,在内部使用 data 数组。其中的每个项目都有一个value和一些用于特定实现的属性,用于确定其在序列中的位置。默认情况下,从此类构造的对象不可迭代。为了定义一个迭代器,我们使用Symbol.iterator并对其进行设置,以便返回的序列根据类的内部实现按顺序排列,而返回的项目只返回它们的value.

在相关说明中,迭代器只是函数,这意味着它们可以像任何其他函数一样被调用(例如,将迭代委托给现有的迭代器),同时也不受Symbol.iterator名称的限制。这允许我们为同一个对象定义多个迭代器。以下是这些概念的示例:

class SpecialList {
  constructor(data) {
    this.data = data;
  }

  [Symbol.iterator]() {
    return this.data[Symbol.iterator]();
  }

  values() {
    return this.data
      .filter(i => i.complete)
      .map(i => i.value)
      [Symbol.iterator]();
  }
}

const myList = new SpecialList([
  { complete: true, value: 'Frank' },
  { complete: true, value: 'Jack' },
  { complete: false },
  { complete: true, value: 'James' },
]);

for (let item of myList) {
  console.log(item); // 将确切数据传递给上面的特别列表构造器
}

for (let item of myList.values()) {
  console.log(item); // 'Frank', 'Jack', 'James'
}

在这个例子中,我们使用data对象的本身数组的迭代器来使我们的SpecialList可迭代,返回data数组的确切值。同时,我们还定义了一个values方法,它本身就是一个迭代器,在数组上使用Array.prototype.filter()Array.prototype.map()。最后,我们返回结果的Symbol.iterator ,只允许对序列中的非空对象进行迭代,并仅返回每个对象的 value