生成器函数

js/generator_functions2026-08-24 19:46:25

虽然自定义迭代器是一个有用的工具,但由于需要显式地维护其内部状态,因此创建时要格外谨慎。生成器函数(Generator 函数)提供了一个强大的替代选择:它允许你定义一个 非连续执行的函数 作为迭代算法。

生成器函数

最初调用时,生成器函数不执行任何代码,而是返回一种称为 生成器 的特殊迭代器。

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
  let iterationCount = 0;
  for (let i = start; i < end; i += step) {
    iterationCount++;
    yield i;
  }
  return iterationCount;
}

生成器函数的语法

生成器函数使用 function* 声明编写。在函数内部使用 yield 和 return 控制迭代器的迭代结果。

当调用 next() 方法消耗该生成器时,生成器函数将执行,直至遇到 yield 关键字,生成器函数退出并且生成相应的值。

下次调用 next() 方法时,将从上次 yield 退出的地方继续执行。其上下文(变量绑定)会在重新进入时保存。

在生成器中执行 return 语句会使生成器结束(即返回的对象的 done 属性将被设置为 true)。如果返回一个值,它将被设置为生成器返回的对象的 value 属性。

与 return 语句类似,如果生成器内部抛出错误,生成器也会结束,除非在生成器的代码体内捕获该错误。

当生成器结束后,后续 next() 调用不会执行生成器的任何代码,只会返回一个形如 {value: undefined, done: true} 的对象。

委托给其他生成器或可迭代对象

yield* 表达式用于委托给另一个生成器或可迭代对象。

function* g1() {
  yield 2;
  yield 3;
  yield 4;
}

function* g2() {
  yield 1;
  yield* g1();
  yield 5;
}

var iterator = g2();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

生成器

生成器函数返回值是一个生成器。

可以根据需要多次调用生成器函数,并且每次都返回一个新的生成器,但每个生成器只能迭代一次。

生成器符合迭代器协议,所以可以将生成器当作迭代器使用:

// it 是一个生成器
let it = makeRangeIterator(1, 10, 2);

let result = it.next();
while (!result.done) {
  console.log(result.value); // 1 3 5 7 9
  result = it.next();
}

console.log(`已迭代序列的大小:${result.value}`); // 5

生成器是可迭代对象

生成器从它们的 [Symbol.iterator]() 方法中返回 this,所以生成器是可迭代对象。

function* makeIterator() {
  yield 1;
  yield 2;
}

// it 是生成器
const it = makeIterator();

// 它有一个 [Symbol.iterator]() 方法返回 it(它自己)
console.log(it[Symbol.iterator]() === it); // true

生成器只能迭代一次

虽然生成器是可迭代对象,但是这个可迭代对象只能迭代一次,因为它每次从 [Symbol.iterator]() 返回的迭代器都是同一个。

如果要迭代多次,需要多次调用生成器函数,产生多个生成器。

function* makeIterator() {
  yield 1;
  yield 2;
}

// it 是生成器
const it = makeIterator();

// 迭代一次 it,会输出 1,2
for (const itItem of it) {
  console.log(itItem);
}

// it 对象只能迭代一次,下面的代码什么都不会做
for (const itItem of it) {
  console.log(itItem);
}

使用生成器函数自定义的可迭代对象

在可迭代对象 [Symbol.iterator]() 方法处使用生成器函数,那么每次调用时都会返回一个新的生成器(迭代器)。

这种写法避免了手动创建迭代器。

var myIterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  },
};

自定义的可迭代对象可用 for...of 循环或者展开语法进行迭代。

for (let value of myIterable) {
  console.log(value);
}
// 1
// 2
// 3

[...myIterable]; // [1, 2, 3]

接收 next() 方法的参数

对于迭代器,在调用 next() 方法时可以传递一个参数。对于使用生成器函数产生的迭代器,也可以接收到这个参数。

只需要在 yield 前面接收参数即可。这样当迭代器调用 next() 方法从 yield 处继续时,传给 next() 方法的参数会赋值给 yield 前面的变量。

传给第一个 next() 的值会被忽略。(因为第一次调用 next() 方法时不是从 yield 处继续)。

function* fibonacci() {
  let current = 0;
  let next = 1;
  while (true) {
    const reset = yield current;
    [current, next] = [next, next + current];
    if (reset) {
      current = 0;
      next = 1;
    }
  }
}

const sequence = fibonacci();
console.log(sequence.next().value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3
console.log(sequence.next().value); // 5
console.log(sequence.next().value); // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2

相关条目

迭代协议

参考

迭代器和生成器
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_generators

function*
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/function*

Generator
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Generator

yield
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/yield

yield*
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/yield*

本页目录