知识点:
Generator function 生成函数的调用和启动不一样,调用之后,函数不没有启动,只有再调用gen.next()的时候,才进入函数体,函数才启动。
基本知识点回顾:
函数作为参数调用的时候,是在调用函数的时候,会先对参数求值,比如:
wrapper11(fn11())
上述代码如果是普通函数(非Generator function),那么一定是先执行完fn11()的,比如:
function startTest11() {
function fn11() {
console.log('startTest11 ==>> fn11 :: enter.')
return 'fn11'
}
function wrapper11(obj) {
console.log('startTest11 ==>> wrapper11 :: enter.')
return [obj]
}
const arr11 = [...wrapper11(fn11())]
console.log('startTest11 ==>> wrapper :: arr11 = ', arr11)
}
startTest11()
// 输出如下:
// startTest11 ==>> fn11 :: enter.
// startTest11 ==>> wrapper11 :: enter.
// startTest11 ==>> wrapper :: arr11 = [ 'fn11' ]
疑问产生:
但是,如果是生产函数,貌似不太一样,注意啊,是貌似:
function startTest10() {
function * fn10() {
console.log('startTest10 ==>> fn10 :: enter.');
yield 1;
yield 2;
return 'fn10';
}
function * wrapper10(genObj) {
console.log('startTest10 ==>> wrapper10 :: enter.');
yield 'a';
yield 'b';
const rst = yield* genObj
console.log('startTest10 ==>> wrapper10 :: rst = ', rst);
}
// wrapper(fn())
const arr10 = [...wrapper10(fn10())];
console.log('startTest10 ==>> end :: arr10 = ', arr10);
}
// 输出如下:
// startTest10 ==>> wrapper10 :: enter.
// startTest10 ==>> fn10 :: enter.
// startTest10 ==>> wrapper10 :: rst = fn10
// startTest10 ==>> end :: arr10 = [ 'a', 'b', 1, 2 ]
注意看,在调用const arr10 = [...wrapper10(fn10())];,从日志看,貌似是违背了理解的常规的函数调用逻辑,没有先输出startTest10 ==>> fn10 :: enter.,反而是先输出startTest10 ==>> wrapper10 :: enter.,感觉有些不可思议。
其实,我理解的不是这样的,还是先调用了fn10(),只是fn10是一个生成函数,生成函数需要在调用其返回值时一个迭代器,genObj.next()才启动。因此,刚掉完fn10()的时候,函数并没有内容输出。 [...wrapper10(fn10())]会自动遍历wrapper10结果的迭代器,此时完成第一层的遍历。
第二层遍历是wrapper10函数体中的yield*,const rst = yield* genObj,这个类似一个for of 循环:
// 参数genObj是一个迭代器
function* gen(genObj) {
yield* genObj
}
相当于
// 参数genObj是一个迭代器
function* gen(genObj) {
for(const item of genObj) {
yield item
}
}
此时才启动进入fn10的函数的返回值,因此看上去像是函数参数方法调用晚了。其实不是,只是因为启动晚了而已。