跳到主要内容

迭代器

包含 yield 语句而不包含 return 语句的函数称为产生器。虽然不使用 return 语句,但是 yield 语句会将输出值保存在产生器并返回,返回值同时是一个迭代器,用户可以使用迭代方法遍历输出的所有值。

语法格式如下:

function* myFunc(arg_1, arg_2,..., arg_n) {
// 执行一些语句...
yield (object);
// 执行一些语句 ... 但不能包含 return 语句返回值
}

close()方法用于关闭产生器,一旦关闭,将会产生以下结果:

  • 产生器函数内的任何 finally 语句都将终止
  • 如果 finally 语句抛出一个异常,而不是 StopIteration ,那么该异常将会被传播给 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 关键字可以让生成器停止和开始执行,也是生成器最有用的地方。生成器函数在遇到 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);
}

使用 yield*实现递归算法

生成器可以产生自身

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() 方法都可以用于强制生成器进入关闭状态。