八、ES6系列之Generator
前面我们介绍过 Iterator(迭代器),本文主要了解 Generator (生成器)函数。
生成器是一种返回迭代器的函数,通过 function 关键字后的星号(*)来表示,函数中会用到新的关键字 yield。
1 | function* createIterator(item) { |
或者使用 for…of 方法统一遍历
1 | for (var v of it) { |
从上例可以看到,使用了 ES6 的生成器,明显简化迭代器的创建过程,给生成器函数 createIterator()传入一个 item 数组,函数内部,for 循环不断从数组中生成新的元素放入迭代器中,每遇到一个 yield 语句循环都会停止;每次调用迭代器的 next()方法,循环便继续运行并停止在下一条 yield 语句处。
生成器函数 createIterator()执行后创建的迭代器赋值给变量 it,变量 it 就作为这个迭代器的引用。既可以通过手工调用 next()方法来执行迭代过程,也可以使用 for..of..来完成迭代过程。
生成器的种类
一般分类四种:
- 生成器函数声明:
1 | function* genFunc() { ··· } |
- 生成器函数表达式:
1 | const genFunc = function* () { ··· }; |
- 对象字面量中的生成器方法定义:
1 | const obj = { |
- 类定义中的生成器方法定义(类声明或类表达式):
1 | class MyClass { |
next 方法的传参
在生成器函数内部使用 yield 关键字暂停,yield 表达式本身没有返回值,或者说总是返回 undefined。在该函数执行返回的迭代器上调用 next()获得暂停时的返回值。其实 next()方法可以接收参数,这个参数的值会代替生成器内部上一条 yield 语句的返回值。
1 | // 生成器 |
这个功能有很重要的语法意义。Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过 next 方法的参数,就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值。也就是说,可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。
Generator.prototype.throw()
throw 方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。
1 | var g = function* () { |
上面代码中,遍历器对象 i 连续抛出两个错误。第一个错误被 Generator 函数体内的 catch 语句捕获。i 第二次抛出错误,由于 Generator 函数内部的 catch 语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就被抛出了 Generator 函数体,被函数体外的 catch 语句捕获
Generator.prototype.return()
return()方法,可以返回给定的值,并且终结遍历 Generator 函数。
1 | function* createIterator(item) { |
上面代码中,遍历器对象 it 调用 return()方法后,返回值的 value 属性就是 return()方法的参数。并且,Generator 函数的遍历就终止了,返回值的 done 属性为 true,以后再调用 next()方法,done 属性总是返回 true。
总结
最后总结下,生成器是创建迭代器的函数,生成器函数内部有 yield 关键字来提供暂停接口,作为创建的迭代器调用 next()方法执行的节点。生成器函数与普通函数的区别是前者在 function 关键字后有星号(*),并且生成器函数执行后会创建一个新的迭代器实例,其他则和普通函数一样,可以传参和返回值。迭代器的 next()方法可以传入参数,传入的参数值将会代替迭代器内上一条 yield 语句的返回值。