包含 yield 语句而不包含 return 语句的函数称为产生器。虽然不使用 return 语句,但是 yield 语句会将输出值保存在产生器并返回,返回值同时是一个迭代器,用户可以使用迭代方法遍历输出的所有值。
语法格式如下:
function* myFunc(arg_1, arg_2,..., arg_n) {
// 执行一些语句...
yield (object);
// 执行一些语句 ... 但不能包含 return 语句返回值
}
close()方法用于关闭产生器,一旦关闭,将会产生以下结果:
close()方法的语法格式如下:
gen.close();
其中, gen 是一个产生器。
例如下面的代码:
function* generator() {
var i = 0;
while (i < 10) {
//产生输出
yield i;
i++;
}
}
// 获取产生器
var g = generator();
// 迭代
for (var j = 0; j < 10; j++) {
console.log('值为:' + g.next() + ' ');
if (j > 5) {
g.close();
}
}
注意这段代码,当 j 大于5时(也就是 j = 6
时 )关闭产生器,此时已经迭代了0~6,这时关闭了产生器, for 循环也会终止。
目前,仅 Firefox 浏览器支持产生器。
生成器的形式是一个函数,函数名称前面加一个星号 *
表示它是一个生成器。只要是可以定义 函数的地方,就可以定义生成器。
// 数的地方, 就可以定义生成器
// 生成器函数声明
function* generatorFn() {}
// 生成器函数表达式
let generatorFn = function* () {};
// 作为对象字面量方法的生成器函数
let foo = { *generatorFn() {} }; // 作为类实例方法的生成器函数
class Foo {
*generatorFn() {}
} // 作为类静态方法的生成器函数
class Bar {
static *generatorFn() {}
}
调用生成器函数会产生一个生成器对象。生成器对象一开始处于暂停执行 suspended
的状态。
生成器对象也实现了 Iterator 接口,因此具有 next()
方法。调用这个方法会让生成器 开始或恢复执行。
next()
方法的返回值类似于迭代器,有一个 done
属性和一个 value
属性。函数体为空的生成器 函数中间不会停留,调用一次 next()
就会让生成器到达 done: true
状态。
function* generatorFn() {}
let generatorObject = generatorFn();
// generatorFn{<suspended>}
console.log(generatorObject);
// { done: true, value: undefined }
console.log(generatorObject.next());
yield 关键字可以让生成器停止和开始执行,也是生成器最有用的地方。生成器函数在遇到 yield 关键字之前会正常执行。遇到这个关键字后,执行会停止,函数作用域的状态会被保留。停止执行的生 成器函数只能通过在生成器对象上调用 next()方法来恢复执行。
function* generatorFn() {
yield;
}
let generatorObject = generatorFn();
// { done: false, value: undefined }
console.log(generatorObject.next());
// { done: true, value: undefined }
console.log(generatorObject.next());
此时的 yield 关键字有点像函数的中间返回语句,它生成的值会出现在 next()方法返回的对象里。 通过 yield 关键字退出的生成器函数会处在 done: false
状态;通过 return 关键字退出的生成器函数会处于 done: true 状态。
yield 关键字只能在生成器函数内部使用,用在其它地方会抛出错误。类似函数的 return 关键字, yield 关键字必须直接位于生成器函数定义中,出现在嵌套的非生成器函数中会抛出语法错误。
function* generatorFn(initial) {
console.log(initial);
console.log(yield);
console.log(yield);
}
let generatorObject = generatorFn('foo');
generatorObject.next('bar'); // foo
generatorObject.next('baz'); // baz
generatorObject.next('qux'); // qux
function* generatorFn() {
return yield 'foo';
}
let generatorObject = generatorFn();
console.log(generatorObject.next()); // { done: false, value: 'foo' }
console.log(generatorObject.next('bar')); // { done: true, value: 'bar' }
function* range(start, end) {
while (end > start) {
yield start++;
}
}
for (const x of range(4, 7)) {
console.log(x);
} // 4
// 5 // 6
function* zeroes(n) {
while (n--) {
yield 0;
}
}
// [0, 0, 0, 0, 0, 0, 0, 0]
console.log(Array.from(zeroes(8)));
可以使用星号增强 yield 的行为,让它能够迭代一个可迭代对象,从而一次产出一个值。
function* generatorFn() {
yield* [1, 2];
yield* [3, 4];
yield* [5, 6];
}
for (const x of generatorFn()) {
// 1 , 2 ,3 ,4 ,5 ,6
console.log(x);
}
生成器可以产生自身
function* nTimes(n) {
if (n > 0) {
yield* nTimes(n - 1);
yield n - 1;
}
}
for (const x of nTimes(3)) {
// 0 , 1 , 2
console.log(x);
}
每个生成器首先都会从新创建的生成器对象产出每个值,然后再产出一个整数。结果就是生成器函数会递归地减少计数器值,并实例化另一个生成器对象。从最顶层来看,这就相当于创 建一个可迭代对象并返回递增的整数。 使用递归生成器结构和 yield*
可以优雅地表达递归算法。
return()
和 throw()
方法都可以用于强制生成器进入关闭状态。