javascript中的Generator(生成器)使用(上)

632 阅读2分钟

「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战」 摘要:本文主要写了javascript中的生成器函数概念,语法及与默认迭代器直接的关系,还有yield的使用,各部分附有示例代码参考。

1.生成器的概念及语法

生成器的形式是一个函数,语法为在function和函数名之间有一个*号,在函数体中可以使用yield关键字去暂停函数的执行。调用该函数将返回一个生成器对象,该生成器对象实现了iterator接口,所以,该生成器也是一个迭代器,有next()方法去遍历 开始运行整个函数体,遇到yield时暂停执行,再次调用next时,从函数上次中止的地方接着运行,以此类推,直到函数体结束。 参考代码如下,其中g1为生成器函数,it1为调用该生成器函数产生的迭代器:

function* g1(){
    console.log("第一次yield");
    yield;
    console.log("第二次yield");
    yield;
}
let it1 = g1();
console.log(it1.next());
console.log(it1.next());
console.log(it1.next());
/*输出结果如下:
第一次yield            //第一次调用next()方法是函数开始执行,打印出此行
{ value: undefined, done: false }         //此打印为调用next()的返回值iteratorResult对象
第二次yield            //第二次调用next()方法是函数继续执行,打印出此行
{ value: undefined, done: false }         //此打印为调用next()的返回值iteratorResult对象
{ value: undefined, done: true }          //此打印为调用next()方法,函数已运行完,所以done的value为true
*/

2.用生成器函数给对象自定义默认迭代器

从迭代器那节我们知道,调用对象的Sympol.iterator工厂函数,会返回一个迭代器对象,所以我们也可以给这个工厂函数 声明为一个生成器函数,用来实现自定义默认迭代器,实例代码如下,实现功能和上篇博客的自定义默认迭代器一样,生成10个 1-10直接的随机数:

class RandomValue{
    constructor(number,max){
        this.number = number;
        this.max = max;
    }
    *[Symbol.iterator](){
        let count = 1;
        let number = this.number;
        let max = this.max;
        while(count<=number){
            yield Math.floor(Math.random()*max)+1;
            count++;
        }
    }
};
let r1 = new RandomValue(10,10);
let a1 = new Array();
it1 = r1[Symbol.iterator]();
for (const i of it1) {
    a1.push(i);
}
console.log(a1);
/*输出结果为:
[
   4, 3, 4, 4, 10,
  10, 5, 9, 8,  9
]
 */

3.yield的使用

3.1 yield用来暂停函数的运行,但yield后面可以加表达式作为调用next时返回的value值,不加时则为undefined(从上面的代码结果 可以看到)。所以,yield可以看作是函数的中间返回值。函数暂停时,其作用域会保留。运行的函数结尾时,可使用return来控制返回的value值。上述各点参考代码如下:

function* g2(){
    yield "first";
    yield 3+7;
    return "end";
}
let it2 = g2();
console.log(it2.next());
console.log(it2.next());
console.log(it2.next());
/*输出结果如下:
{ value: 'first', done: false }    //value为第一个yield后面的值
{ value: 10, done: false }         //value为第二个yield后面的值
{ value: 'end', done: true }       //value为return后面的值
*/

3.2 yield后面也可以加*来遍历它后面所跟的迭代器(或者说可迭代对象),如内置类型数组,依次返回数组中的值。也可为生成器函数的调用,即一个生成器函数里可以调用另外一个生成器函数,其中的一个特殊现象就是调用这个生成器函数 本身即递归。参考代码如下,实现功能计数为10,每next一次,计数减一。

function* g3(n){
    if(n>0){
        yield* g3(n-1);
        yield n;
    }
}

let it2 = g3(10);
let a3 = new Array();
for (const i of it2) {
    a3.push(i);
}
console.log(a3);
/*输出结果为:
[
  1, 2, 3, 4,  5,
  6, 7, 8, 9, 10
]
 */