ECMAScript6生成器函数的使用方法及使用技巧解析!

生成器是添加到 ECMAScript 6 中的一种极其灵活的构造,能够在功能块内暂停和恢复代码执行。

生成器函数提供了一个强大的选项:它允许您定义一个包含自己的迭代算法的函数,同时它会自动维护自己的状态。

生成器函数使用 function* 语法定义,并且 * 不受周围空格的影响。生成器可以在任何可以定义函数的地方定义。

function* generator1() {}

注意:箭头函数不能用于定义生成函数。

方法

调用生成器函数会生成一个符合可迭代和迭代器协议的生成器对象。生成器对象以挂起状态开始。与迭代器类似,生成器也实现了迭代器接口。

Generator.prototype.next()

Generator 对象有一个 next() 方法,该方法调用生成器来开始或恢复执行,并返回一个带有 done 和 value 属性的 IteratorResult 对象。默认情况下,返回值为 { done: true, value: undefined }。

function* generator() {
    return 'sample';
}
const v1 = generator();
// 默认的迭代器是自引用的
console.log(v1);    // generator {}
console.log(generator()[Symbol.iterator]());    // generator {}
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 {}
console.log(v1.return("quit")); // { done: true, value: "quit" }
console.log(v1); // generator {}

当 return() 方法不提供参数时,返回对象的 value 属性的值是未定义的。

使用return()方法关闭生成器时,无法恢复,后续调用next()方法返回的值为{done: true, value: undefined}。

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

诸如 for-of 循​​环之类的内置语言结构会忽略状态为 done: true 的 IteratorObject 内部返回的值。

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()

图片[1]-ECMAScript6生成器函数的使用方法及使用技巧解析!-老王博客

生成器还提供了 throw() 方法来将错误注入到生成器中。如果错误未在内部处理,则生成器将关闭。

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

但是生成器函数会在内部处理此错误,因此生成器不会关闭并且可以恢复执行。错误处理会跳过相应的产量中文关键词生成器,因此在这种情况下会跳过一个值。如下:

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 {}
console.log(v.next);    // { value: "instance", done: false}

这里,一个错误被注入到生成器内部,由 yield 关键字抛出,错误在生成器内部的 try-catch 块中处理。此时生成器函数会继续执行,但是下次调用next()方法时,不会生成示例值,而是生成实例值。

注意:如果生成器对象还没有开始执行,那么调用 throw() 抛出的错误将不会在函数内部被捕获,因为它相当于在函数块之外抛出错误。

屈服

ECMAScript 6 提供了 yield 关键字来暂停生成器函数并保留函数范围的状态。生成器对象将通过调用 next() 方法恢复生成器函数的执行。省略表达式返回未定义:

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 循环。

注意:yield 关键字只能在生成器函数内部使用,并且必须在生成器函数定义中,如果它出现在嵌套的非生成器函数中会抛出错误。

还可以使用 yield* 语法来增强 yield 的行为,以委托给另一个生成器或可迭代对象。

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* 表达式可以调用生成器中的其他生成器。

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论