Javascript中Generator(生成器函数)浅析

生成器是ES6新增的功能,标志是加一个*.

1.生成器函数的执行过程

生成器函数和普通函数不一样,普通函数是一旦调用一定会执行完,生成器函数必须通过调用next方法才能执行,执行到yield标记符的时候暂停,此处yield后方的值就是当前next方法返回的value值,是下次调用next方法传入参数的入口值。

<script>
    function *go(a){
        console.log(1);
        //此处的b用来供外界输入进来的
        //这一行实现输入和输出,本次的输出放在yield后面,下次的输入放在yield前面
        let b =  yield a;
        console.log(2);
        let c = yield b;
        console.log(3);
        return c;
    }
    //生成器函数和普通的函数不一样,调用它的话函数并不会立刻执行
    //它会返回此生成器的迭代器,迭代器是一个对象,每调用一次next就可以返回一个值对象
    let it = go("a值");
    console.log(it);//{}
    //next第一次执行不需要参数,传参数没有意义
    let r1 = it.next();
    第一次调用next会返回一个对象,此对象有两个属性,一个是value就是yield后面那个值,一个是done表示是否迭代完成
    console.log(r1);//{ value: 'a值', done: false }
    let r2 = it.next('B值');
    console.log(r2);//{ value: 'B值', done: false }
    let r3 = it.next('C值');
    console.log(r3);//{ value: 'C值', done: true }
</script>

2.生成器函数的参数传递

生成器函数第一次调用next方法不需要参数,有参数也没有意义,因为这时候前面没有yield的语句;以后的调用从上次的yield标志符传入,如果没有参数,则传入undefined:

<script>
    function* foo(x) {
        let a = 2 * (yield(x + 1));
        let b = yield(a / 3);
        return (x + a + b);
    }
    //没传参的情况
    var it=foo(5);
    console.log(it.next());//{ value: 6, done: false }
    console.log(it.next());//{ value: NaN, done: false }
    console.log(it.next());//{ value: NaN, done: true }
    //传参数的情况:
    var it1=foo(5);
    //第一次next传参没意义
    console.log(it1.next());//{ value: 6, done: false }
    console.log(it1.next(6));//{ value: 4, done: false }
    console.log(it1.next(7));//{ value: 24, done: true }
</script>

需要说明的是.next()方法返回的是yield右边表达式的值,.next(x)传递的参数是用参数值代替yield表达式的值给其他语句下一次调用next方法执行.

3.生成器函数的return影响

如果调用next方法后执行了return,那么生成器函数就结束了,后面即使有yield标记符也不会再执行了,此next方法返回对象的done键值为true.

<script>
    function* generator(x) {
        console.log('x', x);
        let a = yield x;
        return 10;
        console.log('a', a);
        let b = yield (x + 1) + a;
        console.log('b',b);
        let c= yield a + b;
        console.log('a + b =', c);
        return a + b;
    }
    generator(10);
    let g = generator(10);
    console.log(g.next());//{ value: 10, done: false }
    console.log(g.next(1000));//{ value: 10, done: true }
    console.log(g.next(50));//{ value: undefined, done: true }
    console.log(g.next());//{ value: undefined, done: true }
</script>

对于for of循环函数,return的值不会被for of循环遍历到:

<script>
    function* generator(x) {
        console.log('x', x);
        let a = yield x;
        console.log('a', a);
        let b = yield (x + 1) + a;
        return "hello!"
        console.log('b',b);
        let c= yield a + b;
        console.log('a + b =', c);
        return a + b;
    }
  for(let item of generator(10)){
      console.log(item)
  }
</script>

for…of遍历函数的item相当于返回next方法的value值,只是最后的return值不返回而已。上面代码的结果为:
x 10
10
a undefined
NaN

4.try catch throw的执行

<script>
    function *go(){

        try{
            yield;
        }catch(e){
            console.log("err",e)
        }
    }
    let it=go();
    it.next();
    try{
        it.throw("出错了");
    }catch(e){
        console.log("外部捕获",e)
    }
    //输出的结果是:"err 出错了"
</script>

5.yield运算优先级

yield运算符的优先级是最低的,其右方表达式的结果运算出来后在执行yield等待,除非用括号限定优先级,比如下列:

<script>
   function* NinjaGenerator(action) {
     const imposter = (yield "Hatori ") + action;   
     yield ("Yoshi (" + imposter + ") " + action);
   }
    
   const ninjaIterator = NinjaGenerator("skulk");
          
   const result1 = ninjaIterator.next(); 
   console.log(result1.value)//Hatori 
   const result2 = ninjaIterator.next("Hanzo");
   console.log(result2.value)//Yoshi (Hanzoskulk) skulk
</script>

result1.value值为Hatori,而result2.value的值为Yoshi (Hanzoskulk) skulk如果把上面的代码第一个yield运算符的括号删掉,变成:

const imposter = yield "Hatori " + action;

那么result1.value值变为Hatori skulk,而result2.value的值为Yoshi (Hanzo) skulk

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值