Generator函数语法解析 (2)

也就是说,如果有yield表达式,则value属性值就是yield表达式后面的指;如果没有yield表达式,value属性值就等于return语句后面的值;如果yield表达式和return语句都不存在的话,则value属性值就等于undefined。举个例子: )

function *gen () { yield 1 yield 2 return 3 } const g = gen() g.next() // {value: 1, done: false} g.next() // {value: 2, done: false} g.next() // {value: 3, done: true} g.next() // {value: undefined, done: true}

根据next运行逻辑再针对这个例子,就很容易理解了。调用gen函数,返回遍历器对象。

第一次调用next方法时,在遇到第一个yield表达式时停止执行,value属性值为1,即yield表达式后面的值,done为false表示遍历没有结束;

第二次调用next方法时,从暂停的yield表达式后开始执行,直到遇到下一个yield表达式后暂停执行,value属性值为2,done为false;

第三次调用next方法时,从上一次暂停的yield表达式后开始执行,由于后面没有yield表达式了,所以遇到return语句时函数执行结束,value属性值为return语句后面的值,done属性值为true表示已经遍历完毕了。

第四次调用next方法时,value属性值就是undefined了,此时done属性为true表示遍历完毕。以后再调用next方法都会是这两个值。

next方法的参数

yield表达式本身没有返回值,或者说总是返回undefined。

function *gen () { var x = yield 'hello world' var y = x / 2 return [x, y] } const g = gen() g.next() // {value: 'hello world', done: false} g.next() // {value: [undefined, NaN], done: true}

从上面代码可以看出,第一次调用next方法时,value属性值是'hello world',第二次调用时,由于变量y的值依赖于变量x,而yield表达式没有返回值,所以返回了undefined给变量x,此时undefined / 2为NaN。

要解决上面的问题,可以给next方法传递参数。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

function *gen () { var x = yield 'hello world' var y = x / 2 return [x, y] } const g = gen() g.next() // {value: 'hello world', done: false} g.next(10) // {value: [10, 5], done: true}

当给第二个next方法传递参数10时,yield表达式的返回值为10,即var x = 10,所以此时变量y为5。

注意,由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。V8引擎直接忽略第一次使用next方法的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上说,第一个next方法用来启动遍历器对象,所以不用带上参数。所以呢,每次使用next方法会比yield表达式要多一次。

如果想要第一次调用next方法时就可以传递参数,可以使用闭包的方式。

// 实际上就是在闭包内部执行了一次next方法 function wrapper (gen) { return function (...args) { const genObj = gen(...args) genObj.next() return genObj } } const generator = wrapper(function *generator () { console.log(`hello ${yield}`) return 'done' }) const a = generator().next('keith') console.log(a) // hello keith, done yield*表达式
如果在Generator函数中调用另一个Generator函数,默认情况下是无效的。 function *foo () { yield 1 } function *gen () { foo() yield 2 } const g = gen() g.next() // {value: 2, done: false} g.next() // {value: undefined, done: true}

从上面代码中可以看出,并没有在yield 1处停止执行。此时就需要使用yield* 表达式。从语法角度上说,如果yield表达式后面跟着遍历器对象,需要在yield表达式后面加上星号,表明它返回的是一个遍历器对象。实际上,yield*表达式是for...of循环的简写,完全可以使用for...of循环来代替yield*表达式

function *foo () { yield 1 } function *gen () { yield* foo() yield 2 } const g = gen() g.next() // {value: 1, done: false} g.next() // {value: 2, done: false} g.next() // {value: undefined, done: true} // 相当于 function *gen () { yield 1 yield 2 } // 相当于 function *gen () { for (let item of foo()) { yield item } yield 2 }

如果直接使用了yield foo(),返回的对象的value属性值为一个遍历器对象。而不是Generator函数的内部状态。

function *foo () { yield 1 } function *gen () { yield foo() yield 2 } const g = gen() g.next() // {value: Generator, done: false} g.next() // {value: 2, done: false} g.next() // {value: undefined, done: true}

另外,任何数据类型(Array, String)只要有Iterator接口,就能够被yield*遍历

const arr = ['a', 'b'] const str = 'keith' function *gen () { yield arr yield* arr yield str yield* str } const g = gen() g.next() // {value: ['a', 'b'], done: false} g.next() // {value: 'a', done: false} g.next() // {value: 'b', done: false} g.next() // {value: 'keith', done: false} g.next() // {value: 'k', done: false} ...

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzdfx.html