生成器是 ECMAScript 6 新增的一个极为灵便的构造,领有一个函数块内暂停和复原代码执行的能力。

生成器函数提供了一个弱小的抉择:它容许你定义一个蕴含自有迭代算法的函数,同时它能够主动保护本人的状态。

应用 function*语法定义生成器函数,而且 * 不受两侧空格的影响。只有是能够定义函数的中央,就能够定义生成器。

function* generator1() {}

留神:箭头函数不能用来定义生成器函数。

办法

调用生成器函数会产生一个合乎可迭代协定和迭代器协定的生成器对象。生成器对象一开始处于暂停执行的状态。与迭代器类似,生成器也实现了 Iterator 接口。

Generator.prototype.next()

Generator 对象具备 next() 办法,调用这个办法会让生成器开始或复原执行,而且返回的是一个 IteratorResult 对象,具备 done 属性和 value 属性。默认状况下,返回的值为 { done: true, value: undefined }

function* generator() {    return 'sample';}const v1 = generator();// 默认的迭代器是自援用的console.log(v1);    // generator {<suspended>}console.log(generator()[Symbol.iterator]());    // generator {<suspended>}console.log(v1.next());    // { value: 'sample', done: true }

生成器还能够通过 next(value) 办法向外部传参,并且这个参数会变成 yield 的后果。

function* generator() {    let v = yield 10;    yield v;}let v = generator();console.log(v.next(1));    // { value: 10, done: false }console.log(v.next(2));    // { value: 2, done: false }

第一次调用 next() 传入的值不会被应用,因为这一次调用时为了开始执行生成器函数,它会执行并返回第一个 yield 10 的后果。第二次 next() 调用,取得了 2 作为后果let v = 2 并返回 yield v

Generator.prototype.return()

Generator 提供的 return() 办法返回给定的值并完结生成器。因而能够应用 return() 办法提前终止生成器。

function* generator() {    yield 'sample';    yield ;    yield 'example';    return 'instance';}const v1 = generator();console.log(v1); // generator {<suspended>}console.log(v1.return("quit")); // { done: true, value: "quit" }console.log(v1); // generator {<closed>}

return() 办法没有提供参数,则返回对象的 value 属性的值为 undefined

当应用 return() 办法敞开了生成器后就无奈复原,后续调用 next() 办法返回的值为 {done: true, value: undefined}

console.log(v1.next()); // {done: true, value: undefined}

for-of 循环等内置语言构造会疏忽状态为 done: trueIteratorObject 外部返回的值。

function* generator() {    yield 'sample';    yield ;    yield 'example';    return 'instance';}let v1 = generator1();for (const x of v1) {    if (x === 'example') {        v1.return("quit");    }    console.log(x);}// sample// undefined// example

Generator.prototype.throw()

生成器还提供了 throw() 办法用来向生成器中注入一个谬误。如果外部未解决该谬误,那么生成器就会敞开。

function* generator() {    yield 'sample';    yield ;    yield 'example';    return 'instance';}const v = generator();console.log(v);    // // generator {<suspended>}try {    v.throw(new Error("Throw Error"));} catch (e) {    console.log(e);    // Error: Throw Error}console.log(v);    // generator {<closed>}

然而生成器函数外部解决了这个谬误,那么生成器就不会敞开,还能够复原执行。错误处理会跳过对应的 yield,因而在这个例子中会跳过一个值。如下所示:

function* generator1() {    for (const x of ["sample", "example", "instance"]) {        try {            yield x;        } catch (e) {            console.log("Error caught!");        }    }}const v = generator();console.log(v.next());    // { value: "sample", done: false}v.throw(new Error("Throw Error"));    // console.log(v);    // generator {<suspended>}console.log(v.next);    // { value: "instance", done: false}

在这里,向生成器外部注入一个谬误,该谬误会被 yield 关键字抛出,并且在生成器外部的 try-catch 语句块中解决了该谬误。此时,生成器函数还是会继续执行,但下次调用 next() 办法就不会产生 example 值,而是产生 instance 值。

留神:如果生成器对象还没有开始执行,那么调用 throw() 抛出的谬误不会在函数外部被捕捉,因为这相当于在函数块内部抛出了谬误。

yield

ECMAScript 6 提供了 yield 关键字用来让生成器函数暂停,并且函数作用域的状态会保留。生成器对象会通过调用 next() 办法让生成器函数复原执行。yield 省略表达式会返回 undefined

function* generator() {    yield 'sample';    yield ;    yield 'example';    return 'instance';}let v1 = generator();console.log(v1.next());    // { value: 'sample', done: false }console.log(v1.next());    // { value: undefined, done: false }console.log(v1.next());    // { value: 'example', done: false }console.log(v1.next());    // { value: 'instance', done: true }console.log(v1.next());    // { value: undefined, done: true }

生成器对象可当成可迭代对象,能够应用 for...of 循环。

function* generator() {    for (const x of ["sample", "example", "instance"]) {        yield x;    }}for (const x of generator1()) {    console.log(x);}// sample// example// instance

留神:yield 关键字只能在生成器函数外部应用且必须位于生成器函数定义中,呈现在嵌套的非生成器函数中会抛出谬误。

还能够应用 yield* 语法加强 yield 的行为,用于委托给另一个 generator 或可迭代的对象。

function* generator() {    for (const v of [1, 2, 3]) {        yield v;    }}

等价于

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

等价于

function* generator1() {        yield 1;        yield 2;    }function* generator2() {    yield* generator1();    yield 3;}

留神,yield* 两侧的空格不影响其行为。

因为 yield* 能够调用另外一个生成器,所以通过 yield* 能够实现递归调用。

function* recs(n) {    if (n > 1) {        yield* f(n-1);    }    yield n;}for (const x of recs(3)) {    console.log(x);}// 1// 2// 3

应用递归生成器构造和 yield* 能够优雅地表白递归算法。

小结

生成器是一种非凡的函数,调用之后会返回一个生成器对象。生成器对象实现了 Iterable 接口,因而利用场景与迭代器一样。生成器反对 yield 关键字,用于暂停执行生成器函数,与 next() 办法搭配产生一系列值。yield* 表达式能够在生成器中调用其它生成器。

更多内容请关注公众号「海人为记