• Generator函数介绍


    Generator函数

    基本概念

    英文意思为 “生成器”。

    • generator函数是es6提供的一种异步编程解决方案,语法行为与传统函数完全不同。从状态上,首先我们把他理解成一种状态机,封装了多个内部状态。另外执行generator函数胡返回一个遍历对象,就是说,generator函数还是一个遍历器对象生成函数,可以一次遍历函数中的每一个状态。

    • generator函数的两个特征

    1. function命令与函数名之间有一个星号;
    2. 函数体内部使用yield语句定义不同的内部状态(yield为“产出”的意思)。

    调用generator函数后,该函数并不执行,返回的也不是函数的运行结果,而是一个指向内部状态的指针对象,也就相当于遍历器对象。下一步必须调用遍历器对象的next方法,使得指针移向下一个状态。就是说每次调用next方法,内部指针就从函数头部或上次停留的地方开始执行,直到遇到吓一跳yield语句或是return语句时停止。yield语句是generator函数的暂停执行,next是恢复执行。

    yield语句

    是generator函数的暂停标志。

    • next方法的运行逻辑:
    1. 遇到yield语句就暂停执行后面的操作,并将紧跟在yield后面表达式的值作为返回的对象的value属性值。
    2. 下次调用next方法时再继续往下执行,直到遇到下一个yield语句;
    3. 如果没有遇到新的yield语句,就一直运行到函数结束,直到遇到return语句为止,并将return语句后面的表达式的值作为返回对象的value属性值;
    4. 如果该函数没有return语句,则返回对象的value属性值为undefined

    注意: yield语句为javascript提供了手动的 “惰性求值”
    yield语句与return语句有相似之处也有区别,相似之处在于都返回紧跟在后面的值,区别在于return语句不具备记忆功能。

    var arr = [1, [[2,3],4],[5,6]];
    var flat = function* (a){
        a.forEach(function(item){
            if(typeof item !== 'number'){
                yield* flat(item);
            }else{
                yield item;
            }
        });
    }
    
    for(var f of flat(arr)){
        console.log(f);
    }
    

    这个例子将会报错,因为forEach方法的参数是个普通的函数,但是里面使用了yield语句,普通函数里面是不允许是使用yield语句的。
    那么我们来修改一下

    var arr = [1, [[2,3],4],[5,6]];
    var flat = function* (a){
        var length = a.length;
        for(var i =0; i < length; i++){
            var item = a[i];
            if(typeof item !== 'number'){
                yield* flat(item);
            }else{
                yield item;
            }
        }  
    };
    
    for(var f of flat(arr)){
        console.log(f);
    }// 1, 2, 3, 4, 5, 6
    
    • 与Iterator接口的关系

    任意一个对象的Symbol.iterator方法等于该对象的遍历器对象生成函数,调用该函数会返回该对象的一个遍历器对象。
    遍历器对象本身也有Symbol.iterator方法,执行后返回自身。

    function* gen(){
        //some code
    }
    var g = gen();
    console.log(g[Symbol.iterator]() === g);
    

    gen是一个generator函数,调用它会生成一个遍历器对象g,他的Symbol.iterator属性也是一个遍历器对象生成函数,执行后返回自己。

    yield* 语句

    如果在generator函数内部调用另一个generator函数,默认情况下是没有效果的。

    • yield*语句,专门用来在一个generator函数里面执行另一个generator函数。

        function* foo(){
            yield 'a';
            yield 'b';
        }
        function* bar(){
            yield 'x';
            yield* foo();
            yield 'y';
        }
        
        for(let v of bar()){
            console.log(v);
        }
      

    上面将会返回x, a, b, y。如果直接调用foo函数的话,将不会有什么效果。

    • 如果yield命令后面没有星号,将返回一个遍历器对象,加上星号后将会返回遍历对象的具体值。yield* 语句相当于在generator函数内部部署了一个for...of循环。

        function* concat(iter1, iter2){
            yield* iter1;
            yield* iter2;
        }
        //等同于
        function* concat(iter1, iter2){
            for(var value of iter1){
                yield value;
            }
             for(var value of iter2){
                yield value;
            }
        }
      

    还应注意的是,如果在遍历数组时,用yield命令将会返回全部都的数组成员,yield* 则返回单个字符:即数组的第一个成员。

    • 如果generator函数有return语句,那么将会返回相应的数据,如果return()不带任何参数,则相应位置返回的值为undefined。

    • yield* 命令可以很方便的取出嵌套数组的所有成员。

        function* iterTree(tree){
            if(Array.isArray(tree)){
                for(let i = 0; i < tree.length; i++){
                    yield* iterTree(tree[i]);
                }
            }else{
                yield tree;
            }
        }
        
        const tree = ['a', ['b', 'c'], ['d', 'e']];
        
        for(let x of iterTree(tree)){
            console.log(x);
        }// a b c d e
      

    Generator函数的this

    Generator函数总是返回一个遍历器,这个遍历器是generator函数的实例。

    function* g(){
        this.a = 11;
    }
    
    let obj = g();
    console.log(obj.a);//undefined
    

    上面的代码中,函数g总是返回遍历器对象,而不是this对象,这时使用new命令就不能生成对应函数的实例,因为函数返回的是一个内部指针。如果想让generator函数能有正常的构造函数,就可以采用:先生成一个空对象,使用bind方法绑定generator函数内部的指针,这样,构造函数调用之后这个空对象就是generator函数的实例对象了。

    function* F(){
        yield this.x = 2;
        yield this.y = 3;
    }
    var obj = {};
    var f = F.bind(obj)();
    
    f.next();
    f.next();
    f.next();
    console.log(obj);//Object{value: 2, done: false}
    

    通过generator函数部署Ajax操作

    function* main(){
        var result = yield request("http://some.url");
        var resp = JSON.parse(result);
        console.log(resp.value);
    }
    
    function request(url){
        makeAjaxCall(url, function(response){
            it.next(response);
        });
    }
    
    var it = main();
    it.next();
    

    上面的main函数就是通过AJAX获取到的数据,makeAjaxCall函数的next方法中必须加上response参数,因为yield语句构成的表达式本身是没有值的,总是等于undefined。

  • 相关阅读:
    置入式广告 场景中并无实际对应物
    文本自动摘要的方法研究
    [翻译]用DataSource控件以外的方法为GridView提供数据
    留个纪念
    新街口
    [翻译]SharePoint2007中创建Forms认证方式的站点
    路不一定是死的
    网站转移小记
    [转载]什么时候使用webservice
    城市周末的夜还是那么美
  • 原文地址:https://www.cnblogs.com/yehui-mmd/p/6771104.html
Copyright © 2020-2023  润新知