• 《红宝书》 |生成器


    生成器拥有在一个函数块内暂停和恢复代码执行的能力

    生成器函数

    生成器的形式是一个函数,而函数名前面加上一个*表示它是一个生成器。只要是可以定义函数的地方,就能定义生成器:

    //生成器函数声明
    function * generatorFn(){}
    
    //生成器函数表达式
    let generatorFn=function * (){}
    
    //作为对象字面量方法的生成器函数
    let foo={
        * generatorFn(){}
    }
    
    //作为类实例方法的生成器函数
    class Foo{
        * generatorFn(){}
    }
    
    //作为类静态方法的生成器函数
    class Foo{
        static * generatorFn(){}
    }
    

    生成器的*不受两侧空格影响箭头函数不能用来定义生成器函数

    生成器对象

    调用生成器函数会产生一个生成器对象:

    //生成器函数
    function * generatorFn(){}
    //生成器对象
    let g=generatorFn()
    

    生成器对象实现了iterator接口,即生成器对象也可称为是迭代器对象。生成器一开始是处于暂停执行(suspended)状态的,调用next()方法能让其开始或恢复执行。

    next()的返回值类似于迭代器,具有donevalue属性。函数体为空的生成器函数调用一次next()就会让生成器对象到达{done:true}状态:

    function * generatorFn(){}
    let g=generatorFn()
    
    console.log(g[Symbol.iterator]) //ƒ [Symbol.iterator]() { [native code] } --表明实现了iterator接口
    console.log(g[Symbol.iterator]())  //generatorFn {<suspended>}    --返回一个生成器,打印值表示一开始是暂停执行
    
    console.log(g)  //generatorFn {<suspended>} --表示一开始是暂停执行
    console.log(g.next) //f next() { [native code] }    --表示生成器对象具备next方法
    console.log(g.next()) //{value: undefined, done: true}  --调用next方法恢复执行
    

    yield基本概念

    yield关键字可以让生成器停止和开始执行。生成器函数在遇到之后执行会停止,函数作用域的状态会被保留,需要在生成器对象上调用next()方法恢复执行:

    function * generatorFn(){
        yield
    }
    let g=generatorFn()
    console.log(g.next())   //{value: undefined, done: false}   --遇到了yield停止执行
    console.log(g.next())   //{value: undefined, done: true}    --接着触发next,恢复执行
    

    通过next()返回的对象中,value属性表示生成器函数的返回值,默认值为undefined。我们可通过yieldreturn指定生成器函数的返回值,不同的点在于,yield返回的对象处于done:false状态,而return返回的对象处在done:true状态:

    function * generatorFn(){
        yield "one";
        yield "two"
        return "three"
    }
    let g=generatorFn()
    console.log(g.next())   //{value: "one", done: false}
    console.log(g.next())   //{value: "two", done: false}
    console.log(g.next())   //{value: "three", done: true}
    

    在生成器对象上调用next()是互不干扰的:

    function * generatorFn(){
        yield "one";
        yield "two"
        return "three"
    }
    let g1=generatorFn()
    let g2=generatorFn()
    console.log(g1.next())   //{value: "one", done: false}
    console.log(g2.next())   //{value: "one", done: false}
    console.log(g2.next())   //{value: "two", done: false}
    console.log(g1.next())   //{value: "two", done: false}
    

    yield关键字只能在生成器函数内部调用,否则抛错:

    //无效
    function * generatorFn(){
        function fnname(){
            yield;
        }
    }
    
    //无效
    function * generatorFn(){
        const fnname=()=>{
            yield;
        }
    }
    

    yield应用

    可迭代对象

    生成器对象显式调用next()的用处不大,把它当做可迭代对象用起来更方便:

    //无效
    function * generatorFn(){
        yield "one";
        yield "two"
        yield "three"
    }
    //这里的generatorFn()就相当于一个生成器对象,因为调用了generatorFn生成器函数
    for( const item of generatorFn()){
        console.log(item)
    }
    

    自定义迭代对象

    生成一个迭代器,执行指定的次数:

    //自定义可迭代对象:生成n个yield
    function * nTime(n){
        //当n减为0时,相当于flase,退出循环
        while(n--){
            yield;
        }
    }
    
    //创建迭代器:nTime(3)
    for(let n of nTime(3)){
        console.log('hello')
    }
    

    实现范围和填充数组

    function * range(start,end){
        while(end>start){
            yield start++
        }
    }
    for(const x of range(4,7)){
        console.log(x)
    }
    

    实现输入输出

    yield会接受传给next()的值:

    function * state(initial){
        console.log(initial)
        console.log(yield)
        console.log(yield)
    }
    let g=state('hello')
    console.log(g.next('one'))  //hello --遇到yield停止执行,所以返回hello
    console.log(g.next('two'))  //two   --再次调用next恢复执行,yield成功接受值并打印
    console.log(g.next('three'))    //three
    

    产生可迭代对象

    可以使用yield*让它能够迭代一个可迭代对象,从而一次产出一个值(星号两侧的值不影响其行为):

    function * generatorFn(){
        yield* [1,2,3]
        yield* [4,5]
    }
    let g=generatorFn()
    for(const x of g){
        console.log(x)
    }
    //1
    //2
    //3
    //4
    //5
    

    生成器作为默认迭代器

    生成器函数和默认迭代器(Symbol.iterator)被调用后都会产生迭代器,所以生成器函数适合作为默认迭代器:

    class Foo{
        constructor(){
            this.value=[1,2,3]
        }
        *[Symbol.iterator](){
            yield* this.value
        }
    }
    
    const f=new Foo()
    for (const x of f){
        console.log(x)
    }
    

    提前终止生成器

    可用过return()throw()提前终止生成器:

    • return()会强制生成器进入关闭状态,提供给return 的值就是终止迭代器对象的值

      function * generatorFn(){
            yiled* [1,2,3] 
      }
      let g=generatorFn()
      console.log(g)              //generatorFn {<suspended>}
      console.log(g.return(4))    //{value: 4, done: true}
      console.log(g)              //generatorFn {<closed>}
      console.log(g.next())       //{value: undefined, done: true}    --只要进入关闭状态,后续调用next都返回done:done
      
    • throw()会在暂停的时候将一个提供的错误注入到生成器对象中,如果错误未被处理,生成器就会关闭:

      function * generatorFn(){
            yiled* [1,2,3] 
      }
      let g=generatorFn()
      
      console.log(g)    //generatorFn {<suspended>}
      try{
          g.throw('foo')
      }catch(e){
          console.log(e)    //foo
      }
      console.log(g)    //generatorFn {<closed>}
      

      如果生成器函数内部处理了这个错误,生成器就不会关闭。错误处理会跳过对应的yiled:

      function * generatorFn(){
            for(const x of [1,2,3]){
                try{
                    yield x;
                }catch(e){
                    console.log('error:'e)
                }
            }
      }
      let g=generatorFn()
      
      console.log(g.next())     //{value: 1, done: false}
      g.throw('foo')            //error:foo
      console.log(g.next())     //{value: 3, done: false}
      

      相关文章:生成器函数的异步应用

  • 相关阅读:
    EasyUI combogrid 赋多个值
    EasyUI 打印当前页
    EasyUI 获取行ID,符合条件的添加样式
    JS 调用存储过程传递参数
    彻底解决Request Too Long的问题
    SQL处理XML
    DataTable排序
    EasyUI 动态生成列加分页
    SQL2012 分页(最新)
    计算数据库中各个表的数据量和每行记录所占用空间
  • 原文地址:https://www.cnblogs.com/sanhuamao/p/14408988.html
Copyright © 2020-2023  润新知