ES6:Generator的来龙去脉

ES6:Generator的来龙去脉

概要

  • Generator函数是ES6提供的一种异步编程解决方法.可以将它理解为一个状态机,封装了多个内部状态。-

  • 执行Generator函数会返回一个遍历器对象,可依次遍历函数内部的每一个状态;

  • Generator函数有两个特征:

    • function 与函数名之间有一个星号;
    • 函数体内部使用yield语句定义不同的内部状态。
  • 示例:

    function*helloWorldGenerator(){
          
          
        yield '听不见!';
        yield '重来!';
        return '很有精神!'
    }
    let hw = helloWorldGenerator();
    
    hw.next();	// { value : '听不见!',done : false }
    hw.next();	// { value : '重来!',done : false }
    hw.next();	// { value : '很有精神!',done : true }
    hw.next();	// { value : undefined , done : true }
    
  • 利用这个特性,可以实现函数的异步编程机制;

yield表达式

  • 遍历器的next方法运行机制如下:

    1. 遇到yield语句就暂停执行后面的操作,返回yield表达式的值作为返回的对象的value属性值;
    2. 下一次调用next时继续执行,知道遇到 下一个yield语句;
    3. 若无yield,直接运行到return语句;
    4. 若无return语句,则返回一个value值为undefined的对象。
  • yield必须放在Generator函数中才能够使用;

  • yield若用在另一个表达式中必须添加圆括号!

    console.log('hello' + yield 123); ///error
    console.log('hello' + (yield 123));  //ok 
    

next方法的参数

  • next方法可以带有一个参数,该参数会被当作上一条语句的返回值。当next不带参数时,则返回undefined;

    function* f(){
          
          
        for(let i=0;ture;i++){
          
          
            let reset = yield i;
            if(reset){
          
          
                i = -1;
            }
        }
    }
    
    let g = f();
    g.next()	//{value : 0,done : false};
    g.next()	//{value : 1,done : false};
    g.next(true)	//{value : 0,done : false};
    

for…of方法

  • for…of循环可以自动遍历Generator函数生成的Iterator对象,从而无需再使用next方法;

    function* foo(){
          
          
        yield 1;
        yield 2;
        yield 3;
        yield 4;
        yield 5;
    }
    
    for (let v of foo()){
          
          
        console.log(v);
    }
    

yield*表达式

  • 若在一个Generator调用另一个Generator函数,默认是没有效果的;

    function* foo(){
          
          
        yield 'a';
        yield 'b';
    }
    
    function bar(){
          
          
        yield 1;
        foo();
        yield 2;
    }
    
    for(let i of bar()){
          
          
        console.log(i);
    }
    
    >>1
    >>2
    
  • 可以看出,直接调用是无法在for…of中执行foo()的;

  • 此时就需要用到yield*语句,因此上面的代码可以修改成这样:

    function* foo(){
          
          
        yield 'a';
        yield 'b';
    }
    
    function bar(){
          
          
        yield 1;
        yield* foo();
        yield 2;
    }
    
    for(let i of bar()){
          
          
        console.log(i);
    }
    
    >>1
    >>'a'
    >>'b'
    >>2
    
  • 如果yield*后面跟着一个数组,就会遍历该数组的成员:

    function* foo(){
          
          
        yield [1,2,3,4,5];
    }
    foo().next();	//[1,2,3,4,5]
    
    function* foo(){
          
          
        yield* [1,2,3,4,5];
    }
    foo().next();	//{value : 1,done : false}
    
  • 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 = [1,[2,3],[4,5]];
    for(let x of iterTree(tree)){
          
          
        console.log(x);
    }
    
    >> 1
    >> 2
    >> 3
    >> 4
    >> 5
    

Generator函数的this

  • Generator函数返回的遍历器为原Generator函数的实例,也继承了原函数的原型;

  • 因此,Generator函数的遍历器无法直接访问原函数的属性;

    function* foo(){
          
          
        this.a = 1;
    }
    
    let obj = foo();
    console.log(obj.a);			//undefined
    
  • 要获得原函数的属性,可通过变通的方法进行获取——使用call方法实现绑定Generator函数内部的this;

    function* foo(){
          
          
        this.a = 1;
        yield this.b = 2;
        yield this.c = 3;
    }
    let obj = {
          
          };
    let bar = foo.call(obj); 
    
    bar.next();		//{value : 2 , done : false};
    bar.next();		//{value : 3 , done : false};
    
    console.log(obj.a)	// 1
    console.log(obj.b)	// 2
    console.log(obj.c)	// 3
    

猜你喜欢

转载自blog.csdn.net/yivisir/article/details/108476987
今日推荐