ES6之Generator对象

生成器真是 ES6 中了不起的一项特性

基本概念

1
2
3
4
5
6
7
function* sayHi(name) {
yield `Hello! ${name}`;
yield `This is ES6 Generator`;
yield `ByeBye!`;
let sum = 3 + 5;
return sum;
}

上面这段代码看起来很像一个函数,我们称之为生成器函数,它与普通函数有很多共同点,主要区别如下:

  1. 普通函数使用 function 声明,而生成器函数使用 function* 声明
  2. 生成器函数体内部,有一种类似 return 的语法:关键字 yield 。二者区别是普通函数只可以 return 一次,而生成器函数可以 yield 多次。在生成器的执行过程中,遇到 yield 表达式立即暂停,后续可恢复执行状态

当需要调用 Generator 函数,我们可以像下面这样:

1
2
3
4
5
6
let message = sayHi('sam');
message.next(); // Object { value: "Hello! sam", done: false }
message.next(); // Object { value: "This is ES6 Generator", done: false }
message.next(); // Object { value: "ByeBye", done: false }
message.next(); // Object { value: 8, done: true }
message.next(); // Object { value: undefined, done: true }

调用 Generator 函数,它并非立即开始运行,而是返回一个已暂停的 Generator 对象(在上面代码中就是 message)。

然后,我们可以调用 Generator 对象的 .next() 方法,以执行函数体内的代码。

Generator对象的 next() 方法语执行逻辑如下:

  1. 正常执行函数体内的代码,遇到 yield 语句,就暂停执行后面的操作,并将紧跟在 yield 关键字后面的那个表达式的值作为返回对象的 value 属性值,返回对象同时还有一个 done 属性值,如果当前执行的代码不是函数体内最后一条语句,done 属性值就为 false,否则 done 属性值为 true
  2. 继续调用 next() 方法时,继续执行函数体内后续代码,直到遇到下一个 yield 语句,重复步骤 1 中的执行逻辑
  3. 如果没有遇到新的 yield 语句,就一直执行到函数体结束
  4. 如果函数有 return 语句,将 return 语句表达式的值做为返回对象的 value 属性值
  5. 如果函数没有 return 语句,返回对象的 value 属性值设置 undefined
  6. 当函数体内所有代码都执行完毕时,再调用 Generator 对象的 next() 方法总是返回 { value: undefiend, done: true}

与 Iterator 接口的关系

任意一个对象的 Symbol.iterator 方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。

由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的 Symbol.iterator 属性,从而使得该对象具有 Iterator 接口

Generator函数执行后,返回一个遍历器对象。该对象本身也具有 Symbol.iterator 属性,执行后返回自身。

next() 方法的参数

yield 语句本身没有任何返回值,或者说总是返回 undefiend 。 Generator 对象的 next() 方法可以带一个参数,该参数就会被当作上一个 yield 语句的返回值。

for…of 循环

for...of 循环·可以自动遍历 Generator 函数调用生成的 Iterator 对象。

1
2
3
4
5
6
7
8
9
10
function* foo() {
yield 1;
yield 2;
yield 3;
return 4;
}
for (le i of foo()) {
console.log(i);
} // 1 2 3 4

Generator.prototype.throw()

Generator 函数返回的遍历器对象,都有一个 throw 方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获

thorw 方法可以接受一个参数,该参数会被 catch 语句接受,建议抛出 Error 对象的实例。

Generator.prototype.return()

Generator 函数返回的遍历器对象,还有一个 return() 方法,可以接受参数,指定返回对象的 value 属性值,并且终止遍历 Generator 函数。如果调用 return() 时不传入参数,那么返回对象的 value 属性值为 undefined.

yield*

如果在一个 Generator 函数内部调用另一个 Generator 函数,需要使用 yield* 语句。

1
2
3
4
function* bar() {
yield* foo();
}
function* foo() {}

作为对象属性的 Generator 函数

如果一个对象的属性是 Generator 函数,可以简写:

1
2
3
4
5
6
7
let obj = {
* test() {}
}
// 等价于
let obj = {
test: function*() {}
}

Generator 函数的 this

Generator函数总是返回一个遍历器,ES6规定这个遍历器是 Generator 函数的实例,也继承了 Generator 函数的 prototype 属性所指向对象上的方法。

Generator 函数不可与 new 命令一起使用,否则会报错。


参考