ES6 Generator函数之基本用法(2)

Generator函数之基本用法(2)

上一篇文章中总结了Generator函数基本概念:
yield表达式,与Iterator接口、for…of循环的关系,next方法,throw方法,return方法等内容。
这篇文章接着上一篇文章继续总结Generator函数的基本用法

(1)yield*表达式

直接在Generator函数内部调用另一个Generator函数,默认情况下是没有效果的:

function* foo() {
  yield 'a';
  yield 'b';
}

function* bar() {
  yield 'x';
  foo();
  yield 'y';
}

for (let v of bar()){
  console.log(v);
}
// "x"
// "y"

如果使用yield命令,则会返回一个遍历器对象:

  function* foo() {
        yield 'a';
        yield 'b';
    }

    function* bar() {
        yield 'x';
        yield foo();
        yield 'y';
    }

    for (let v of bar()){
        console.log(v);
    }
    // "x"
    //一个遍历器对象 foo
    // "y"
1.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"

这时候我们称bar为代理者,foo为被代理者。

2.利用yield*等同于在代理Generator函数内部部署一个for…of循环
(前提是,被代理的Generator函数没有return语句)

以下的代码与上面的yield*写法是等效的:

  function* foo() {
        yield 'a';
        yield 'b';
    }

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

    for (let v of bar()) {
        console.log(v);
    }
    // "x"
    // "a"
    // "b"
    // "y"
3.被代理的Generator函数具有return语句,可以向代理它的Generator函数返回值
  function* foo() {
        yield 'a';
        yield 'b';
        return "foo"
    }

    function* bar() {
        yield 'x';
        let word=yield* foo();
        yield word;
        yield 'y';
    }

    for (let v of bar()) {
        console.log(v);
    }
    // "x"
    // "a"
    // "b"
    // "foo"
    // "y"

另外一个例子:

 function* foo() {
        yield 'a';
        yield 'b';
        return "foo"
    }

    function* bar() {
        yield 'x';
        let word=yield* foo();
        yield word;
        yield 'y';
    }

   let a=[...bar()];
    console.log(a);
    //["x","a","b","foo","y"]
4.yield*后面跟着带有Iterator接口的数据结构
   //字符串
    function* foo() {
        yield* "qwe"
    }
    for (let v of foo()) {
        console.log(v);
    }
    // q
    // w
    // e

    
    //数组
    function* bar() {
        yield* [1, 2, 3]
    }
    for (let v of bar()) {
        console.log(v);
    }
    // 1
    // 2
    // 3

    
    //Set结构
    function* foo1() {
        yield* new Set(["q", "w", "e"])
    }
    for (let v of foo1()) {
        console.log(v);
    }
    // q
    // w
    // e

    
    //Map结构
    function* bar1() {
        yield* new Map([["q", 1], ["w", 2], ["e", 3]])
    }
    for (let v of bar1()) {
        console.log(v);
    }
    // ["q",1]
    // ["w",2]
    // ["e",3]
5.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

(2)Generator函数作为对象属性

如果一个对象的属性是Generator函数,可以写成下面的形式:

let obj = {
  * myGeneratorMethod() {
    ···
  }
};

//等同于
let obj = {
  myGeneratorMethod: function* () {
    // ···
  }
};

(3)Generator函数的this

Generator函数总是返回一个遍历器对象,ES6规定这个遍历器对象是Generator函数的实例:

   function* g() {}
    let obj = g();
    console.log(obj instanceof g)
    // true

Generator函数返回的遍历器会继承Generator函数的prototype对象上的方法:

    function* g() {}

    g.prototype.hello = function () {
        return 'hi!';
    };

    let obj = g();
    console.log(obj.hello()) // 'hi!'

注意,Generator函数返回的是遍历器对象,而不是this对象,因此不能当作普通的构造函数

function* g() {
  this.a = 11;
}

let obj = g();
obj.next();
obj.a // undefined

Generator函数不能和new一起使用,否则会报错:

function* F() {
  yield this.x = 2;
  yield this.y = 3;
}

new F()
// TypeError: F is not a constructor

那么,有没有办法让 Generator 函数返回一个正常的对象实例,既可以用next方法,又可以获得正常的this?

下面是一个变通方法。首先,生成一个空对象,使用call方法绑定 Generator 函数内部的this。这样,构造函数调用以后,这个空对象就是 Generator 函数的实例对象了。

function* F() {
  this.a = 1;
  yield this.b = 2;
  yield this.c = 3;
}
var obj = {};
var f = F.call(obj);

f.next();  // Object {value: 2, done: false}
f.next();  // Object {value: 3, done: false}
f.next();  // Object {value: undefined, done: true}

obj.a // 1
obj.b // 2
obj.c // 3

上面代码中,首先是F内部的this对象绑定obj对象,然后调用它,返回一个 Iterator 对象。这个对象执行三次next方法(因为F内部有两个yield表达式),完成 F 内部所有代码的运行。这时,所有内部属性都绑定在obj对象上了,因此obj对象也就成了F的实例。

上面代码中,执行的是遍历器对象f,但是生成的对象实例是obj,有没有办法将这两个对象统一呢?

一个办法就是将obj换成F.prototype:

function* F() {
  this.a = 1;
  yield this.b = 2;
  yield this.c = 3;
}
var f = F.call(F.prototype);

f.next();  // Object {value: 2, done: false}
f.next();  // Object {value: 3, done: false}
f.next();  // Object {value: undefined, done: true}

f.a // 1
f.b // 2
f.c // 3

(4)应用

1.处理异步操作,改写回调函数

异步操作的同步化表达。处理异步操作时,在写法上可以做到与同步写法类似。

2.控制流管理
3.部署Iterator接口

利用Generator函数可以在任意对象上部署Iterator接口

function* iterEntries(obj) {
  let keys = Object.keys(obj);
  for (let i=0; i < keys.length; i++) {
    let key = keys[i];
    yield [key, obj[key]];
  }
}

let myObj = { foo: 3, bar: 7 };

for (let [key, value] of iterEntries(myObj)) {
  console.log(key, value);
}

// foo 3
// bar 7
4.作为数据结构

参考文献:《ECMAScript 6 入门》阮一峰

猜你喜欢

转载自blog.csdn.net/weixin_42695446/article/details/84566660