function 想必大家都熟烂于心,那什么是 function* 呢?function* 就是上面提到的生成器函数。我们来看个例子,在 Node 的 REPL 中运行:

function* helloWorldGeneratorFunction() {
  yield 'hello';
  yield 'world';
  return '!';
}

var helloWorldGenerator = helloWorldGeneratorFunction();

> helloWorldGenerator.next()
{ value: 'hello', done: false }
> helloWorldGenerator.next()
{ value: 'world', done: false }
> helloWorldGenerator.next()
{ value: '!', done: true }
> helloWorldGenerator.next()
{ value: undefined, done: true }

有几点需要说明:

  1. 生成器函数(GeneratorFunction)执行后会返回一个生成器(Generator)。
  2. 生成器函数内可以使用 yield(或者 yield*, 后面介绍),函数执行到每个 yield 时都会暂停执行并返回 yield 的右值(函数的上下文,如变量绑定等信息会保留),通过调用生成器的 next 方法返回一个包含 value 和 done 的对象,value 为当前返回的值,done 表明函数是否执行结束。每次调用 next,函数会从 yield 的下一个语句继续执行。等到整个函数执行完,next 方法返回 value 变为 undefined,done 变成 true。

稍微改一下上面的例子:

function* helloWorldGeneratorFunction() {
  var hello = yield 'hello';
  console.log(hello);
  var world = yield 'world';
  console.log(world);
  return '!';
}

var helloWorldGenerator = helloWorldGeneratorFunction();

> helloWorldGenerator.next()
{ value: 'hello', done: false }
> helloWorldGenerator.next()
undefined
{ value: 'world', done: false }
> helloWorldGenerator.next()
undefined
{ value: '!', done: true }
> helloWorldGenerator.next()
{ value: undefined, done: true }

上例中,当第一次调用生成器的 next 方法时,函数执行到 yield 'hello' ,会返回 { value: 'hello', done: false },但此时 yield 没有返回值,或者说返回值为 undefined,所以当下一次调用 next 方法时,相当于执行 var hello = undefined; console.log(hello); yield 'world' 。所以 console.log(hello); 会打印 undefined,同理 console.log(world); 也会打印 undefined。

再稍微修改一下上面的例子,在 next 方法中传入参数:

function* helloWorldGeneratorFunction() {
  var hello = yield 'hello';
  console.log(hello);
  var world = yield 'world';
  console.log(world);
  return '!';
}

var helloWorldGenerator = helloWorldGeneratorFunction();

> helloWorldGenerator.next(1)
{ value: 'hello', done: false }
> helloWorldGenerator.next(2)
2
{ value: 'world', done: false }
> helloWorldGenerator.next(3)
3
{ value: '!', done: true }
> helloWorldGenerator.next(4)
{ value: undefined, done: true }

我们往 next 方法里传入了参数,那么该参数就成为上一个 yield 语句的返回值。即上例中调用 helloWorldGenerator.next(2) 相当于执行了 var hello = 2; console.log(hello); yield 'world'。由于 next 方法的参数当作了上一个 yield 语句的返回值,所以第一次调用 next 方法时传入的参数将被忽略。

生成器函数也是函数,所以拥有所有函数的特性。比如作用域,闭包,遇到第一个 return 会执行结束等等。生成器函数又有些普通函数没有的特性,如可以使用 yield 并且 yield 只能在生成器函数内使用,如果在普通函数内使用 yield 将会报错。