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

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了"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值