yield* 表达式 和 Generator 函数中的 this

目录

yield* 表达式

Generator 函数中的 this


yield* 表达式

如果在 Generator 函数内部调用另一个 Generator 函数,需要在前者的函数内部自己手动完成遍历。

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

function* bar() {
  yield 'x';
  // 手动遍历 foo()
  for (let i of foo()) {
    console.log(i);
  }
  yield 'y';
}

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

ES6 提供 yield* 函数,用来在另一个 Generator 函数内部调用另一个 Generator 函数。

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

// 等同于
function* bar() {
  yield 'x';
  yield 'a';
  yield 'b';
  yield 'y';
}

// 等同于
function* bar() {
  yield 'x';
  for (let v of foo()) {
    yield v;
  }
  yield 'y';
}

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

可以看出,yield* 的作用就是遍历。

function* inner() {
  yield 'hello!';
}

function* outer1() {
  yield 'open';
  yield inner();
  yield 'close';
}

var gen = outer1()
gen.next().value // "open"
gen.next().value // 返回一个遍历器对象
gen.next().value // "close"

function* outer2() {
  yield 'open'
  yield* inner()
  yield 'close'
}

var gen = outer2()
gen.next().value // "open"
gen.next().value // "hello!"
gen.next().value // "close"

从语法角度看,如果 yield* 表达式后面跟的是一个遍历器对象,那么就需要在 yield 后面加一个星号,表明它返回的是一个遍历器对象。

yield* 后面的 Generator 函数(如果这个函数没有 return 语句的话),等同于在 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 表达式,不会输出 return 表达式。

function* g1() {
    yield 1;
    yield 2;
    return 3;
}

function* g2() {
    yield 4;
    yield* g1();
    yield 5;
}

for (var k of g2()) {
    console.log(k);
}
// 4 1 2 5

上面代码不会将 g1 函数的 return 值输出。

function* g1() {
    yield 1;
    yield 2;
    return 3;
}

function* g2() {
    yield 4;
    yield yield* g1();
    yield 5;
}
for (var k of g1()) {
    console.log(k);
}
// 1 2

for (var k of g2()) {
    console.log(k);
}
// 4 1 2 3 5

let opr = g2();
console.log(opr.next());
console.log(opr.next());
console.log(opr.next());
console.log(opr.next());
console.log(opr.next());
console.log(opr.next());
console.log(opr.next());
// {value: 4, done: false}
// {value: 1, done: false}
// {value: 2, done: false}
// {value: 3, done: false}
// {value: 5, done: false}
// {value: undefined, done: true}
// {value: undefined, done: true}

如果 yield* 后面的 Generator 函数有 return 语句,则需要用变量来接收这个返回值(var val = yield* iterator)。

如果 yield* 后面跟的是一个数组,则会遍历所有数组成员。如果不加星号,返回的就是整个数组。

function* gen(){
  yield* ["a", "b", "c"];
}
gen().next() // { value:"a", done:false }

function* g() {
    yield [1, 2];
}
console.log([...g()]);      // [[1, 2]]
console.log([...gen()]);    // [1, 2]

g() 方法返回的遍历器对象,调用 next() 方法返回的是 yield 后面的表达式 => 数组 [1, 2];gen() 方法返回的遍历器对象,调用 next() 方法返回的是数组每一项。

任何部署了 Iterator 接口的数据结构,都可以被 yield* 遍历。

let read = (function* () {
  yield 'hello';
  yield* 'hello';
})();

read.next().value // "hello"
read.next().value // "h"

用 yield* 命令遍历嵌套数组。

function* iterTree(tree) {
    if (Array.isArray(tree)) {
        for (let i = 0; i < tree.length; i++) {
            yield* iterTree(tree[i]);
        }
    } else {
        yield tree;
    }
}
let tree = [1, 2, [3, 4], [[5]]];
for (let k of iterTree(tree)) {
    console.log(k);
}
// 1 2 3 4 5
console.log([...iterTree(tree)]);
// [1, 2, 3, 4, 5]

作为对象属性的 Generator 函数

let obj = {
    *a() {
        yield 1
    }
};
let opr = obj.a();
console.log(opr.next());
// {value: 1, done: false}

Generator 函数中的 this

Generator 函数总是返回一个遍历器对象,ES6 规定这个遍历器是 Generator 函数的实例,也继承了 Generator 函数 prototype 上的方法。

function* g() { }
g.prototype.sayHello = function () {
    console.log('Hello world');
}
let obj = g();
console.log(obj instanceof g);    // true
obj.sayHello();                   // Hello world

Generator 函数和普通函数的区别:不能使用 new 命令,否则会报错;Generator 函数不会返回 this 对象,返回的总是遍历器对象。

function s() {
    this.a = 1;
}
var obj = new s();
console.log(obj.a);    // 1

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

Generator 函数 g 在 this 上面绑定了一个属性,但是作为 g 的实例 obj 却拿不到这个属性。

使用 call() 来绑定 Generator 函数内部 this(除了使用 call,还可以使用 apply、bind,都可以改变函数内部 this 指向)。

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

Generator 函数返回的遍历器对象,是 Generator 函数的实例,那是这个实例就可以获取到 Generator 函数原型上的属性。由此,可以把 Generator 函数内部的 this 指针指向改为 Generator 函数的原型,那么遍历器对象自己就可以继承这些属性了。

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

值得注意的是,如果上面代码没有调用一次 next 方法,f.a 依旧是 undefined。因为这样 F() 调用Generator 函数仅仅返回了一个遍历器对象,并没有开始执行内部代码,this 指针上面并没有挂 a 属性,只有当代码运行了才会有 a 属性。

改造 Generator 函数,可以使用 new 命令。

function* gen() {
  this.a = 1;
  yield this.b = 2;
  yield this.c = 3;
}

function F() {
  return gen.call(gen.prototype);
}

var f = new F();
// ...

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值