yield

426 阅读3分钟

前言

由于Generator函数返回的遍历对象只有在调用next()方法的时候才会遍历下一个内部状态,就相当于提供了一种可以暂停执行的函数,yield表达式作为暂停标志就应运而生

定义与运行逻辑

yield关键字可以控制生成器Generator函数的停止和开始执行,也只能在生成器函数内部使用,可以说是生成器最重要最核心的一部分。

  • 当生成器函数遇到yield关键字时,执行就会立即停止并保留此时函数作用域的状态
  • 此时的yield关键字有点像函数的中间返回语句,yield会把紧跟其后的那个表达式的值作为返回对象的value属性值
  • 当生成器对象调用next()方法时,就会得到上一个yield返回对象的value属性值,且此时的生成器函数会处在done:false状态,意味着调用next()方法后生成器函数就会摆脱暂停状态继续执行(通过return关键词退出的生成器函数会处于done:true状态)
  • 如果没有再遇到yield表达式,就会一直运行到函数结束,直到遇到return语句为止,并将return语句后面的表达式值作为返回对象的value属性值
  • 该函数若没有return语句,则返回...当然是返回undefined啊

示例与应用

写者觉得读析具体的代码比看密密麻麻的文字要高效许多,有疑问上机试一番,豁然开朗

使用yield时生成器函数状态(done: false)与返回值

// 创建生成器函数
function* generatorFn() {
    yield 1;
    yield 'bar';
}

// 构建生成器对象
let generatorObj = generatorFn();

// 生成器对象调用next()函数,输出返回值
console.log(generatorObj.next());
// { value: 1, done: false }
console.log(generatorObj.next());
// { value: 'bar', done: false }
console.log(generatorObj.next());
// { value: undefined, done: true }

yield关键字必须直接位于生成器函数定义中

// 有效
function* validG() {
    yield;
}
let vG = validG();
console.log(vG.next());
// { value: undefined, done: false }

// 无效
function* invalidGa() {
    function a() {
        yield;
    }
}
let iGa = invalidGa();
console.log(iGa.next());
// { value: undefined, done: true }
// 注意和上一个示例对比,当把yield关键字置于嵌套的非生成器函数中,这里虽然没有报错,但是返
// 回的生成器函数状态为done: true,这说明yield在这里并没有起作用,读者可以试着注释掉yield,
// 输出依旧为{ value: undefined, done: true }

// 以上为依据,可以在yield关键字后加上返回值1,试试运行
function* invalidGa() {
    function a() {
        yield 1;
    }
}
let iGa = invalidGa();
console.log(iGa.next());
// SyntaxError: Unexpected number

使用yield输入和输出

function* gFn() {
    return yield 'foo';
}

let gObj = gFn();

// 第一次调用next()函数时即使输入参数也不会被使用,因为这一次调用是为了开始执行生成器函数
console.log(gObj.next()); 
// { value: 'foo', done: false }
console.log(gObj.next('bar'));
// { value: 'bar', done: true }

利用yield关键字的生成器可作为可迭代对象

function* nTimes(n) {
    while(n--) {
        yield;
    }
}

for(let _ of nTimes(3)) {
    console.log("foo");
}
// foo
// foo
// foo

利用yield产生可迭代对象

function* gFn() {
    yield* [1, 2, 3];
    // 星号(*)用于增强yield的行为,让它能够迭代一个可迭代对象
}

let gObj = gFn();

for(const x of gObj) {    
// 等价于 const x 0f gFn()

    console.log(x);
}
// 1
// 2
// 3

yield实现递归算法

// 创造一个可迭代对象并返回递增的整数
function* nTimes(n) {
    if (n > 0) {
        yield* nTimes(n - 1);
        yield n - 1;
    }
}

for (const x of nTimes(3)){
    console.log(x);
}
// 0
// 1
// 2

参考文献

《JavaScript高级程序设计》(第四版)

后话

这是第二篇啦,最近决定不考研冲前端,现在前端刚入门,基础还是很差的,每天都能学到新的东西,这种感觉真的非常棒,就是作为一个生物专业的大三狗,啊,我还有遗传学期中考试,大学不会有人上课还听课吧,不会吧......不会吧......不会吧......笑不出来了......啊!!!