生成器

生成器声明

生成器函数声明

function* generation(){

}

生成器函数表达式

let generatorFn = function * (){

}

作为对象字面量方法的生成器函数

let foo={
    * generatorFn(){

    }
}

作为类实例方法的生成器函数

class Foo{
    * generatorFn(){

    }
}

调用生成器函数

调用生成器函数会产生一个生成器对象。生成器对象一开始处于暂停执行的状态。与迭代器相似,生成器对象也实现了Iterator接口,因此具有next()方法。调用这个方法会让生成器开始或恢复执行。

function *generatorFn(){
    
}
const g = generatorFn();

console.log(g);
console.log(g.next());

Next()方法的返回值类似于迭代器,有一个done属性和一个value实行。函数体为空的生成器函数中间不会停留,调用一次next()就会让生成器到达done:true状态

其中value属性是生气函数的返回值,默认值是undefined,可以通过生成器函数的返回值指定:

function * generatorFn(){
    return 'foo';
}
let generatorObject = generatorFn();

console.log(generatorObject);
console.log(generatorObject.next()); //{done:true,value:'foo'};

通过yield中断执行

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

function* generatorFn(){
    yield;
}
let generatorObject = generatorFn();

console.log(generatorObject.next());
console.log(generatorObject.next());Function* generatorFn()

此时的yield关键字有点像函数的中间返回语句,它生成的值会出现在next()方法返回的对象里。通过yield关键字退出的生成器啊还是农户会处在done:false状态。通过return关键字退出的生成器函数会处于done:true状态。

function* generatorFn(){
    yield 'foo';
    yield 'bar';
    return 'baz';
}

let generatorObject = generatorFn();

console.log(generatorObject.next());
console.log(generatorObject.next());
console.log(generatorObject.next());

生成器函数内部的执行流程会针对每个生成器对象区分作用域。在一个生成器对象上调用next()不会影响其他生成器。

function* generatorFn(){
    yield 'foo';
    yield 'bar';
    return 'baz';
}

for( const x of generatorFn()){
    console.log(x);
}

可以使用星号增强yield的行为

让它能够迭代一个可迭代对象,从而一次产出一个值。

//等价的generatorFn();
function * generatorFn() {
    for(const x of [1,2,3]){
        yield x;
    }
}

function generatorFn(){
    yield *[1,2,3];
}

因为yield*实际上只是将一个可迭代对象序列化为一连串可以单独产出的值,所以这跟把yield放到一个循环里没什么不同。

使用yield*实现递归

function * nTimes(n){
    if(n>0){
        yield* nTimes(n-1);
        yield n-1;
    }
}
for (const x of nTimes(3)){
    console.log(x);
}

此例中,每个生成器首先都会从新创建的生成器对象产出每个值,然后再产出一个整数。结果就是生成器函数会递归地减少计数器值,并实例化另一个生成器对象。

生成器作为默认迭代器

class Foo{
    constructor() {
        this.values=[1,2,3];
    }
    * [Symbol.iterator](){
      yield *this.values;
    }
}
let ei=new Foo();
for(let i of ei){
    console.log(i);
}