发布于 2026-01-06 3 阅读
0

JavaScript 中的迭代器

JavaScript 中的迭代器

很多时候,你醒来后意识到今天要遍历一个数组,甚至可能是多个数组。但你并不担心,因为你以前做过,而且也没那么难。你有很多选择,可以使用传统的循环,或者使用强大的 ` map, reduce`运算符fitler。或者,你也可以使用 ` iterators.`

Iterator是一种允许我们遍历列表或集合的设计模式。在 JavaScript 中,和大多数事物一样,它们都是以对象的形式实现的。在深入探讨细节之前,这里有一个简单的例子。

const arr = [1, 2, 3, 4, 5];

for (const num of arr) {
  console.log(num);
}
Enter fullscreen mode Exit fullscreen mode

使用for..of循环,您可以遍历任何object实现了该iterable协议的对象。

Iterable协议

为了遵循此协议,对象必须定义一个特殊方法@@iterator(作为Symbol.iterator键),该方法不接受任何参数并返回一个对象,该对象本身应该遵循该iterator协议。

Iterator协议

为了遵循此协议,对象必须定义一个名为 `method` 的方法next,该方法本身返回一个具有两个属性的对象:

  1. value:当前迭代项
  2. done:一个布尔值,表示迭代是否结束。done=true表示迭代已结束。

数组、字符串、映射、集合、类型化数组都遵循迭代器协议。

实施这些协议

这是一个返回一个值的函数,iterable它允许我们遍历前几个n自然数。

function numbers(till = 100) {
  let i = 0;
  const iteratorFx = () => {
    const iterator = {
      next() {
        i += 1;
        if (i <= till) {
          return { done: false, value: i };
        }
        return { done: true };
      },
    };
    return iterator;
  };
  return {
    [Symbol.iterator]: iteratorFx,
  };
}

const numbersTill10 = numbers(10);
for (const i for numbersTill10) {
  // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
}
Enter fullscreen mode Exit fullscreen mode

@@iterator方法仅在循环开始时调用一次for..of。因此,以下代码与上述代码相同。

for (const i for numbers(10)) {
  // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
}
Enter fullscreen mode Exit fullscreen mode

既然我们知道该对象有一个next实现迭代细节的方法,我们就可以自己调用这个方法。

const numbersTill5 = number(5);
numbersTill5.next(); // { done: false, value : 1 }
numbersTill5.next(); // { done: false, value : 2 }
numbersTill5.next(); // { done: false, value : 3 }
numbersTill5.next(); // { done: false, value : 4 }
numbersTill5.next(); // { done: false, value : 5 }
numbersTill5.next(); // { done: true }
Enter fullscreen mode Exit fullscreen mode

这可以通过向next方法传递值来从外部控制迭代。

我们可以像上面那样实现自定义迭代器。但是,JavaScript 还提供了另一种创建迭代器的方法iterables

发电机

生成器是一种特殊的函数,调用它时会返回一个Generator对象。该generator对象遵循迭代协议。因此,要使用生成器实现上述示例,

function* generateNumbers(till = 100) {
  let i = 1;
  while (i <= till) {
    yield i;
    i += 1;
  }
}

const numbersTill10 = generateNumbers(10); // iterator
// rest is same
Enter fullscreen mode Exit fullscreen mode

yield(此处)发送的值ivalue存储在next方法返回的对象中。生成器完成后会返回该对象{ done: true }

从上面的例子可以很清楚地看出,生成器提供了一种简洁的创建方式iterables。它们抽象掉了协议,我们只需要关注迭代逻辑即可。

结论

既然我们这篇文章开头用夸张的语言描述了遍历数组,那么给出一个涉及数组的例子也是理所应当的。数组Arrays已经存在iterable,所以我们将创建一个iterable值映射器。

function* mapOver(arr, mapper = (v) => v) {
  for (let i = 0; i < arr.length; i += 1) {
    yield mapper(arr[i]);
  }
}

const twices = mapOver([...numbers(5)], (v) => v + 2);
for (const num of twices) {
  // 2, 4, 6, 8, 10
}
Enter fullscreen mode Exit fullscreen mode

扩展运算符(...)顾名思义,作用于iterables.

文章来源:https://dev.to/akshendra/iterators-in-javascript-3kg9