生成器函数
生成器对象(Generator Object)是一种『运行,暂停,运行,暂停,…,运行,结束』的模式,其中每次暂停都会生成一个值。生成器对象由生成器函数(Generator Function)返回创建。
先来一个简单的例子。
1 | // 生成器函数 |
注意上面生成器函数的定义方式,不同于上一篇总结中的定义方式,这里我们把号放在了函数名前,而上一篇总结中放到了function关键字后,实际上这两种方式都是合法的,在这里我们推荐使用函数名前加号,这样表示生成器函数实际上还是一种函数,只不过是一种特殊的函数。
return关键字
在生成器函数中,同样也是可以使用return关键字的。
1 | function *gen() { |
注意这里最后一行在返回数字3之后,done的值直接为done,而不是之前的必须等到生成一个undefined才得到done。
此外在实际使用过程中并不推荐在生成器函数中使用return关键字,因为在大多数情形中生成器函数都是在一个循环中生成值,因此return返回的值可能永远不能获取。
yield关键字
yield关键字会生成一个值,同时下一次调用next()传入的值将替代上一次的yield表达式,如果yield不显式生成值,则会生成undefined,如果next()不传入任何值,则该yield表达式将被undefined替代。
1 | // 注意这里foo不是一个生成器函数 |
请读者再把上面解释yield特性的加粗段落再读一遍,结合上述代码充分理解一下:
首先我们定义了一个普通函数foo()
,该函数做最简单的传值输出;
然后我们定义了生成器函数bar()
,还生成器一共依次生成两个值,由于未显示声明任何值,因此都是返回undefined;
后续代码中我们生成了一个生成器对象,并依次调用了三次next()
,关于三次调用next()
的结果,很好理解,所有返回值都是undefined
,第三次done
表示已经生成结束。但是我们注意到第三次调用next()
时,调用了foo()函数,yield
关键字会生成一个值,同时下一次调用next()
传入的值将替代上一次的yield
表达式,第一次调用时不发生任何事,第二次调用时我们没有传入任何值,因此一个yield表达式的值就是undefined
,但是第三次调用时我们传入了'test'
,所以第二个yield表达式的值就是'test'
,生成器继续执行,即foo('test')
上面的文字有些绕口,为了更好地理解yield,我们再举一个例子来理解yield关键字。
生成器对象和生成器函数
1 | function *foo(x) { |
注意,生成器函数的也是可以传值的,这里传值的用法并不与普通函数有任何区别。
我们对最后三行逐行进行分析:
第一次it.next()
时,一开始传入的初始值为5,生成器函数中的第一次yield(x + 1)
生成的是6
,这个毫无疑问;
第二次it.next(12)
,传入了12
,12
替换了的第一个yield
表达式,因此y的值变成了2 * 12
也就是24
,因此第二次生成的值是yield(y / 3)
也就是8
;
第三次it.next(13)
,传入13
,13
替换第二个yield
表达式,因此z等于13
,因此第三次生成的值是yield(x + y + z)
,5 + 24 + 13
即42
。
结合for of使用
需要注意的是,在绝大多数情况下,生成器函数都是循环生成值,并结合for of使用,具体可以查阅[参考3]中生成器对象和生成器函数小节中的斐波那契数列例子。
知识点总结
- 理解生成器的概念;
- 充分理解yield关键字,知道在生成器中yield和return的区别;
- 熟练结合for of使用生成器。