理解javascript中Generator生成器函数

604 阅读3分钟

1. 定义生成器函数:

在关键字function后面加上一个星号(*)创建一个生成器函数。在生成器函数内使用yield生成独立的值。

function * WeaponGenerator(){
    yield "Katana";
    yield "Wakizashi";
}

2. 调用生成器函数

调用生成器函数并不会执行生成器函数,而是创建一个迭代器(iterator)的对象,该迭代器用于控制生成器的执行。

const weaponsIterator = WeaponGenerator()//调用生成器函数,返回一个迭代

a. 调用迭代器next方法向生成器请求值。

迭代器调用next方法,向生成器请求一个值。生成器调用next调用后,开始执行代码,当代码执行到yield关键字时,就会返回一个对象,对象包含一个属性value为生成器的值,一个属性done指示生成器是否到所有值都生成完。每当生成一个当前值后,生成器会挂起,等待下一次请求。

const result1 = weaponsIterator.next()//调用迭代器的next方法向生成器请求一个值
console.log(result1)//{ value: 'Katana', done: false }
const result2 = weaponsIterator.next()//调用迭代器的next方法向生成器请求一个值
console.log(result2)//{ value: 'Wakizashi', done: false }
const result3 = weaponsIterator.next()//调用迭代器的next方法向生成器请求一个值
console.log(result3)//{ value: undefined, done: true }
const result4 = weaponsIterator.next()//调用迭代器的next方法向生成器请求一个值
console.log(result4)//{ value: undefined, done: true }

b. 使用while循环访问迭代器。

next方法返回一个对象,对象包含一个属性value为生成器的值,一个属性done指示生成器值是否都生成完毕。可以使用while循环来迭代生成器生成的值序列

let item;
//每次循环都从生成器中取值,然后输出该值,当生成器不再生成值时,就停止迭代。
while(!(item = weaponsIterator.next()).done){
    console.log(item.value);
}

c.使用for-of循环,是对迭代器进行迭代的语法糖。

for(var item of weaponsIterator){
    console.log(item)//直接返回生成器的值
}

d. 把执行权交给下一个生成器

在迭代器中使用yield*操作符,程序会跳到另一个生成器上执行。

function * warriorGenerator(){
    yield "Sun Tzu";
    yield* NinjaGenerator();//将执行权交给另一个生成器
    yield "Genghis Khan"
}

function * NinjaGenerator(){
    yield "Hattori";
    yield "Yoshi";
}

for(var warrior of warriorGenerator()){
    console.log(warrior)
}
//Sun Tzu
//Hattori
//Yoshi
//Genghis Khan

e. 用生成器生成ID序列

function * IdGenerator(){
    let id = 0;
    while(true){
        yield ++id;
    }
}

const idIterator = IdGenerator();
const ninja1 = {id: idIterator.next().value};
const ninja2 = {id: idIterator.next().value};
const ninja3 = {id: idIterator.next().value};
const ninja4 = {id: idIterator.next().value};

在标准函数中不应该使用无限循环代码,但是在生成器中没有问题!当生成器遇到yield语句生成器会挂起执行知道下次调用next方法。

3. 与生成器交互

a.作为生成器函数参数发送值,如普通函数一样,调用函数并传入实参。

b.使用next方法向生成器发送值,把生成器函数从挂起状态恢复到执行状态。生成器把这个值作为当前挂起的表达式的值

function * NinjaGenerator(action){
    const imposter1 = yield (action);
    const imposter2 = yield (imposter1  + action)
    yield (imposter1 + imposter2+ action)
}
const ninjaIterator = NinjaGenerator("skulk")
const result1 = ninjaIterator.next('aaa').value;//传不传参数没有影响
const result2 = ninjaIterator.next("bbb").value;
const result3 = ninjaIterator.next("ccc").value;

console.log(result1)//skulk
console.log(result2)//bbbskulk
console.log(result3)//bbbcccskulk