ES6(ECMAScript 2015)引入了生成器(Generators),这是一个非常强大的特性,它允许你定义一个函数,这个函数可以被暂停和恢复。生成器函数返回一个迭代器对象,该对象可以在每次调用其 next() 方法时执行到下一个yield表达式,并返回一个具有 value 和 done 属性的对象。
Generator 的基本语法
定义一个生成器函数使用 function*
语法,或者在类的方法前加 *。在函数体内部,你可以使用yield关键字来暂停执行,并将值传递给调用者。
function* generatorFunction() {
yield 'Hello';
yield 'World';
return 'End';
}
const iterator = generatorFunction();
console.log(iterator.next()); // { value: 'Hello', done: false }
console.log(iterator.next()); // { value: 'World', done: false }
console.log(iterator.next()); // { value: 'End', done: true }
console.log(iterator.next()); // { value: undefined, done: true }
yield 表达式
yield 用于生成器函数中,表示函数在此处暂停,并且会返回 yield 后面的表达式的值。当再次调用 next() 时,函数会从上次暂停的地方继续执行,直到遇到下一个 yield 或到达函数的末尾。
发送值给生成器
除了获取生成器产生的值,还可以通过 next() 方法向生成器发送值。第一次调用 next() 时传入的值会被忽略,因为没有 yield 表达式接收它。但是之后的 next() 调用可以将值传递给生成器中的 yield 表达式。
function* echoGenerator() {
while (true) {
let value = yield;
console.log(value);
}
}
const echo = echoGenerator();
echo.next(); // 启动生成器,首次调用next()不传递参数
echo.next('Hi'); // 输出: Hi
使用for...of循环
由于生成器返回的是一个迭代器对象,因此可以直接在for...of循环中使用生成器函数。
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
for (let value of generateSequence(1, 3)) {
console.log(value); // 输出: 1, 2, 3
}
Generator 的应用场景
- 异步编程:尽管 Promises 和 async/await 是处理异步代码的主要方式,但在某些情况下,生成器与协程库(如co)一起使用可以简化异步流程控制。
- 数据流控制:生成器可以用来实现复杂的迭代模式,例如无限序列、树遍历等。
- 懒加载数据:只在需要的时候才生成数据,这对于处理大数据集特别有用。
总之,生成器提供了一种优雅的方式来编写可以逐步执行并产生一系列值的函数,这为JavaScript带来了更丰富的编程范式。