关于JS中的迭代器与生成器

71 阅读3分钟

迭代器,生成器!!!!!

这是两个一直没太深入研究的东西,终于,今天总算入门了一下!

先说迭代,首先,说到迭代我们第一反应就是数组,因为数组就是最常见的可迭代对象,那么什么又是可迭代对象呢,需满足以下条件:

  1. 具有迭代器方法;(一般使用[Symbol.iterator]作为属性名)
  2. 迭代器方法必须放回迭代器对象;
  3. 迭代器对象必须包含有next方法;
  4. next方法必须返回包含value属性和done属性的对象。

满足这些条件后,我们迭代可迭代对象诸如数组时,通过调用迭代器方法生成一个迭代器对象,然后一直调用迭代器对象的next方法知道迭代结束,最后done属性返回true。

那为什么要把迭代器跟生成器放在一起总结呢,原因是生成器函数经常被用来作为迭代器的迭代方法(大概是这样?我也不知道,犀牛书说有这个用途~),接下来就来一起看看生成器:

生成器函数通过在普通函数前加上星号*来实现,包含但不限于以下几种形式:

// 函数声明
function* generatorFn(){}
// 对象字面量简写
{
*g(){}
}

生成器函数的内部包含若干代码和yield语句,yield语句后面跟的值就是每次next方法返回的value值(类似return一样提供一个值),ex:

// 1.声明生成器函数
function* generatorFn(){
    yield 1;
    yield 2;
    yield 3;
}
// 2.执行函数,并不会执行函数体内的语句,而是生成一个生成器对象
let gF = generatorFn()
// 3.调用next方法,此时开始从头执行函数体内的语句,直到遇见yield停止,此时返回一个对象,内容为{value:yield语句的值,done:false}
gF.next()       // {value:1,done:false}
// 4.继续调用,从上一个yield之后开始,直到下一个yield
gF.next()       // {value:2,done:false}
gF.next()       // {value:3,done:false}
// 5.全部迭代完毕,此时返回的value为undefined,done为true,表示迭代已完成
gF.next()       // {value:undefined,done:true}

看到这里,其实就可以猜到为什么要把生成器跟迭代器一起讲了

我们知道,迭代器方法返回一个迭代器对象,这个对象里应该有next方法,而next方法又需要返回带有value跟done属性的对象

那这里的生成器不是刚好就符合吗,所以我们就完全可以拿生成器函数作为迭代器方法,因此有如下写法:

        // 1.定义类
        class MyIterableObject {
            constructor(){
                
            }
            // 2.使用生成器函数作为迭代器方法
            *[Symbol.iterator]() {
                for(let i=1;i<=3;i++){
                    yield i
                }
            }
        }
        // 3.创建类实例
        let mIO = new MyIterableObject
        // 4.把迭代器对象保存给变量
        let itrt = mIO[Symbol.iterator]()
        // 5.依次执行,与上个代码块中同样的效果
        console.log(itrt.next());       // {value:1,done:false}
        console.log(itrt.next());       // {value:2,done:false}
        console.log(itrt.next());       // {value:3,done:false}
        console.log(itrt.next());       // {value:undefined,done:true}

ps:补充,当一次次调用next时,一遇到yield语句会立即停止,但在for循环中,for循环体里的语句又都会被执行,不管是在yield前后(如下),这点不知道为什么?留个坑,以后懂了再回来更新。或者有哪位大佬路过刚好看到这,还请赐教一下~

        class OddNumberIterable {
            constructor(maxNum) {
                this.maxNum = maxNum;
                this.current = 1;
            }       
            *[Symbol.iterator]() {
                for(;this.current<=this.maxNum;this.current+=2) {
                      console.log(this.current,'before');
                    yield this.current;
                    console.log(this.current,'after');
                }
            }
        }
        let oddNumbers = new OddNumberIterable(10);
        for (let num of oddNumbers) {
            console.log(num);
        }