在 JavaScript 中,生成器函数 Generator Function 是一种特殊的函数,它允许你在函数执行过程中暂停和恢复。生成器函数通过 function* 语法定义,并使用 yield 关键字来控制函数的执行流程。生成器函数返回一个生成器对象,该对象遵循迭代器协议,可以逐步生成值。
以下是生成器函数的详细解析:
1. 基本语法
生成器函数使用 function* 定义,函数体内可以使用 yield 关键字来暂停函数的执行并返回一个值。
function* myGenerator() {yield 1;yield 2;yield 3;
}
function*:定义生成器函数的关键字。
yield:暂停函数执行并返回一个值。每次调用生成器对象的 next() 方法时,函数会从上次暂停的地方继续执行。
2. 生成器对象
生成器函数调用时不会立即执行函数体,而是返回一个生成器对象。这个生成器对象是一个迭代器,可以通过 next() 方法逐步执行生成器函数。
const gen = myGenerator();console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
next():恢复生成器函数的执行,直到遇到下一个 yield 或函数结束。
返回一个对象,包含两个属性:
value:yield 表达式的值。
done:布尔值,表示生成器函数是否已经执行完毕。
3. yield 关键字
yield 是生成器函数的核心关键字,它的作用如下:
1. 暂停函数执行:当生成器函数执行到 yield 时,会暂停执行并返回 yield 后面的值;
2. 恢复函数执行:当调用生成器对象的 next() 方法时,函数会从上次暂停的地方继续执行,直到遇到下一个 yield 或函数结束;
function* myGenerator() {yield "Hello";yield "World";
}const gen = myGenerator();console.log(gen.next().value); // 'Hello'
console.log(gen.next().value); // 'World'
console.log(gen.next().done); // true
4. yield* 表达式
yield* 用于委托给另一个生成器或可迭代对象(如数组、字符串等)。它允许在一个生成器函数中调用另一个生成器函数。
function* generator1() {yield 1;yield 2;
}function* generator2() {yield* generator1(); // 委托给 generator1yield 3;
}const gen = generator2();console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
console.log(gen.next().done); // true
5. 生成器函数的特性
5.1. 惰性求值
生成器函数是惰性的,只有在调用 next() 时才会执行。这使得生成器非常适合处理大量数据或无限序列。
function* infiniteSequence() {let i = 0;while (true) {yield i++;}
}const gen = infiniteSequence();console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// 可以无限调用
5.2. 双向通信
生成器函数不仅可以通过 yield 返回值,还可以通过 next() 方法接收外部传入的值。
function* generator() {const x = yield "Hello";yield x;
}const gen = generator();console.log(gen.next().value); // 'Hello'
console.log(gen.next(42).value); // 42
第一次调用 next() 时,生成器函数执行到 yield 'Hello' 并暂停。
第二次调用 next(42) 时,42 会作为 yield 表达式的值传入,生成器函数继续执行。
5.3. 提前终止
生成器对象提供了 return() 和 throw() 方法,可以提前终止生成器函数的执行。
return(value):终止生成器函数并返回指定的值。
throw(error):在生成器函数内部抛出一个错误。
function* generator() {yield 1;yield 2;yield 3;
}const gen = generator();console.log(gen.next().value); // 1
console.log(gen.return(42).value); // 42
console.log(gen.next().done); // true
6. 生成器函数的应用场景
6.1. 惰性求值
生成器函数非常适合处理大量数据或无限序列,因为它只在需要时生成值。
function* fibonacci() {let [prev, curr] = [0, 1];while (true) {yield curr;[prev, curr] = [curr, prev + curr];}
}const fib = fibonacci();console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
console.log(fib.next().value); // 5
6.2. 异步编程
在 async/await 出现之前,生成器函数常用于简化异步编程。通过结合 Promise 和生成器,可以实现类似于 async/await 的效果。
function* asyncGenerator() {const result1 = yield new Promise((resolve) =>setTimeout(() => resolve(1), 1000));const result2 = yield new Promise((resolve) =>setTimeout(() => resolve(2), 1000));return result1 + result2;
}function runGenerator(generator) {const gen = generator();function handle(result) {if (result.done) return Promise.resolve(result.value);return Promise.resolve(result.value).then((res) => {return handle(gen.next(res));});}return handle(gen.next());
}runGenerator(asyncGenerator).then(console.log); // 3
6.3. 自定义迭代器
生成器函数可以用于创建自定义迭代器,简化迭代器的实现。
const myIterable = {*[Symbol.iterator]() {yield 1;yield 2;yield 3;},
};for (const value of myIterable) {console.log(value); // 1, 2, 3
}
7. 总结
1. 生成器函数使用 function* 定义,通过 yield 暂停和恢复执行;
2. 生成器函数返回一个生成器对象,该对象是一个迭代器,可以通过 next() 方法逐步执行;
3. yield* 用于委托给另一个生成器或可迭代对象;
4. 生成器函数适用于惰性求值、异步编程和自定义迭代器等场景;
生成器函数提供了一种强大的控制流机制,使得你可以更灵活地管理函数的执行过程。