Skip to content

使用JavaScript生成器函数生成范围

生成器函数

JavaScript ES6生成器允许您定义可以退出并稍后重新进入的函数,同时保留其上下文(变量绑定)。它们使用function*function关键字后跟一个星号)进行定义,并使用yield表达式返回其结果。例如:

function* generateRange(end, start = 0, step = 1) {
  let x = start - step;
  while(x < end - step) yield x += step;
}

const gen5 = generateRange(5);
let x = gen5.next();

while (!x.done) {
  console.log(x.value);
  x = gen5.next();
} // 输出:0, 1, 2, 3, 4

在上面的示例中,我们定义了一个生成器函数generateRange,它将返回startend之间的每个值,每次增加step。我们使用生成器对象调用Generator.prototype.next(),直到它返回{value: undefined, done: true},以迭代生成器产生的值。

Symbol.iterator

Symbol.iterator指定了对象的默认迭代器。通常情况下,Symbol.iterator使用生成器函数来实现。例如:

const iterableXx = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
  }
};

```js
console.log([...iterableX]); // [1, 2]

如你在这个例子中所看到的,通过将一个生成器函数赋值给对象的Symbol.iterator属性,可以使对象成为可迭代的。这在你想要迭代一些任意的数据或者创建一个可迭代的对象并在内部使用生成器函数时非常方便。

将所有内容整合在一起

了解了这两个概念的工作原理后,我们可以将它们结合起来创建一个类似于Python或Ruby的范围生成器:

const range = (end, start = 0, step = 1) => {
  function* generateRange() {
    let x = start - step;
    while(x < end - step) yield x += step;
  }
  return {
    [Symbol.iterator]: generateRange
  };
}

console.log([...range(7)]); // [0, 1, 2, 3, 4, 5, 6]
for (let i of range(8, 2, 2)) console.log(i); // Logs: 2, 4, 6