从零开始学_JavaScript_系列(55)——Generator函数(3)yield*表达式

来源:互联网 发布:汤姆大叔 javascript 编辑:程序博客网 时间:2024/06/08 12:23

4、yield*表达式

4.1、基础

简单来说,yield*表达式就是在一个Generator函数内嵌套另外一个Generator函数。

于是在遍历的过程中,当在第一个Generator函数内遇见第二个Generator函数后,就会先停止遍历第一个Generator函数,先遍历完第二个Generator函数,然后再恢复。

如代码:

let g1 = function*() {    yield 1;    yield* g2();    yield 2;}let g2 = function*() {    yield "a";    yield "b";}let foo = g1();for (let i of foo) {    console.log(i)}// 1// a// b// 2

如上,简单暴力通俗易懂。

假如不是yield* g2(),而是yield g2()会发生什么事情呢?

  1. 首先,g2()会返回一个遍历器对象,毫无疑问;
  2. 其次,yield表达式会使得该遍历器对象作为next的返回值来返回;
  3. 因此最终结果是1——》g2()的遍历器——》2,代码就略了不写

4.2、递归

因为yield*表达式的存在,因此遍历器可以递归自己,代码十分简单:

let g1 = function*(count) {    console.log("count:" + count)    if (count > 3) {        return    }    yield 1;    yield 2;    yield* g1(count + 1);}let foo = g1(1);for (let i of foo) {    console.log(i)}// count:1// 1// 2// count:2// 1// 2// count:3// 1// 2// count:4

4.3、有Iterator接口的数据结构

yield*表达式可以遍历Generator函数,原因是Generator函数有Iterator接口,相当于对Generator函数的遍历器执行了for...of

那么yield*表达式能不能遍历非Generator函数,但是也有Iterator接口的数据结构呢?显然也是可以的。

如代码:

let g1 = function*() {    yield* [1, 2]    yield* "ab"}let foo = g1();for (let i of foo) {    console.log(i)}// 1// 2// "a"// "b"

也可以对自定义数据结构生效,只要他有Iterator接口即可:

function Test() {    let arr = [3, 2, 1]    function Iterator() {        let index = 0        // 该对象有next方法,调用后返回一个当前索引下的值        this.next = function () {            let obj = {}            if (index < 3) {                obj.value = arr[index]                obj.done = false                index++            } else {                obj.value = undefined                obj.done = true            }            return obj        }        // 返回他自己        return this    }    // 遍历器接口    this[Symbol.iterator] = function () {        // 创建一个遍历器对象(Iterator不是关键词)        let temp = new Iterator()        // 返回他        return temp    }}let m = new Test()let g1 = function*() {    yield* m}let foo = g1();for (let i of foo) {    console.log(i)}// 3// 2// 1

4.4、返回值

  1. Generator函数是返回值是他的遍历器;
  2. 遍历器的返回值是对象,有value和done属性;
  3. yield表达式的返回值是根据遍历器的next的参数决定;
  4. 那么yield*表达式的返回值是什么呢?

答案是根据被遍历函数的return所决定;

最简单的示例如代码:

let g1 = function*() {    console.log(yield* g2())}let g2 = function*() {    yield 'a';    return 'b';}let foo = g1();for (let i of foo) {    console.log(i)}// 'a'// 'b'

那么,是否还记得Generator函数的返回值在什么时候起作用?

可以回去看看1.1,return是在done第一次变为true时,value属性的值。

因此,yield*的值,取决于遍历器在遍历结束,done变为true时,value属性的值,如以下代码自定义了一个数据结构,这个数据结构在done变为true时是有值的。

function G() {    let arr = [3, 2, 1]    function Iterator() {        let index = 0        this.next = function () {            let obj = {}            if (index < 3) {                obj.value = arr[index]                obj.done = false                index++            } else {                // 这里与之前的例子不同                obj.value = '自定义数据结构的done变为true了'                obj.done = true            }            return obj        }    }    this[Symbol.iterator] = function () {        let temp = new Iterator()        // 返回他        return temp    }}let g = new G()let g1 = function*() {    console.log(yield* g)}let foo = g1();for (let i of foo) {    console.log(i)}// 3// 2// 1// "自定义数据结构的done变为true了"
阅读全文
0 0