JavaScript迭代器学习笔记

131 阅读2分钟

迭代器

迭代器模式

迭代器模式描述了一个方案,一些可以被迭代的结构实现了正式的interable接口并可以通过迭代器interator消费被称为可迭代结构,任何实现了interable接口的结构都能够被interator对象消费,interator对象是按需创建的一次性结构,每一个迭代器对象关联了一个可迭代对象,迭代器会暴露出其关联对象的api,迭代器不需要知道关联对象的具体结构,他只需要调用对应的api连续获取可迭代对象的值。

js中的迭代器

可迭代协议(interable接口)

上面说了实现了可迭代接口的结构称为可迭代结构,实现了可迭代协议(interable接口)要求能够支持迭代,并且能够实现生成实现了迭代器协议(interator接口)的能够,在ecmscript中这就要求我们暴露出一个属性用来生成对象,js中使用Symbol类型来标识这一属性[Symbol.interator],使用原生语言如for...of,数组的结构赋值,扩展操作符等迭代对象时会默认调用这一个属性来生成迭代器对象。

迭代器协议(interator接口)

迭代器协议是一个一次性对象,他通过调用迭代器api来获取关联对象的数值,在js中迭代器对象调用next()方法来获取可迭代结构的值,next()对象会获取到一个迭代结果对象其中包含了valuedone属性,value属性获取的是当前关联的可迭代对象的信息,而done属性是一个布尔值,表示这个迭代器关联的对象是否迭代完毕

js中的一些迭代方法默认调用对象中的[Symbol.iterator]属性,这个属性一般是一个工厂函数,调用会生成一个迭代器对象,迭代器对象是一种独立的、一次性的对象,它的根本属性是包含了一个next()方法,在进行迭代时迭代器对象会调用next方法,这个方法会返回迭代结果对象,我们可以先实现一个简单的实现迭代器接口的例子:

class example {
        [Symbol.iterator](){
            return {
                a:1,
                next(){
                    if(this.a < 10){
                        return {
                            value:this.a++,
                            done:false,
                        }
                    }
                    else{
                        return{
                            value:undefined,
                            done:true
                        }
                    }
                }
            }
        }
    }
    
   let examplea = new example()
   for(let i of examplea){
         console.log(i)
       }

代码运行结果:

image.png

这里的for...of结构调用了example类中的构造器工厂函数[Symbol.iterator]生成了迭代器,迭代器调用了next()方法,当返回的对象中的done为ture之后循环结束

image.png

迭代器的独立性

我们可以通过默认的构造器工厂函数生成多个构造器,各个生成器之间并不会相互影响

let array = [1,3,6]
        let itr = array[Symbol.iterator]();
        let itr2 = array[Symbol.iterator]();
        console.log(itr.next())
        console.log(itr.next())
        console.log(itr.next())
        console.log(itr2.next())

运行结果

image.png

迭代器的一次性

当一个迭代器遍历完成之后再次调用构造器就只会返回固定的数值{value:undefined,done:true},之后再次调用这个迭代器的next()方法只会返回相同的数值。

迭代器与关联对象之间的关系

构造器关联的可迭代对象并非是快照式的,而是一个类似指针指向关联对象,当关联对象的机构改变之后迭代器也的迭代结果也会相应的改变。

let array = [1,3,6]
        let itr = array[Symbol.iterator]();
        console.log(itr.next())
        console.log(itr.next())
        console.log(itr.next())
        array.push(19)
        console.log(itr.next())

运行结果

image.png

提前退出迭代

我们可以提前将循环结束,通过在构造器对象中加入return()可以让迭代器提前中止循环

    class example {
        constructor(num){
            this.num = num;
        }
        [Symbol.iterator](){
            let a = 1, number = this.num
            return {
                next(){
                    if(a < number){
                        return {
                            value:a++,
                            done:false,
                        }
                    }
                    else{
                        return{
                            value:undefined,
                            done:true
                        }
                    }
                },
                return(){
                    console.log('有内鬼,终止交易')
                    return {
                        value:undefined,
                        done:false
                    }
                }
                }
            }
        }
    
    let exampleA = new example(9);
    for(let i of exampleA){
        console.log(i)
        if(i == 6){
            break;
        }
    }

运行结果:

image.png

并不是所有的实现了迭代器接口的对象都可以中断迭代,只有实现了return()方法的对象可以中断迭代。

迭代器的使用

原生语言如for..of,解构赋值,扩展操作符,Array.from()等会在后台自动调用对象的默认迭代器的工厂函数

let exampleA = new example(10)
        let arrayOne = [2,3,4,5,6,7]
        arrayOne[Symbol.iterator] = ()=>{
            return{
                next(){
                    return{
                        value:'this is a test',
                        done:false
                    }
                }
            }
        }
        let arrayIntor = arrayOne[Symbol.iterator]()
        let [a,b,c] = arrayOne
        let arrayTwo = Array.from(exampleA)
        console.log(...exampleA)
        console.log(`a:${a},b:${b},c:${c}`)
        console.log('arrayTwo',arrayTwo)

运行结果

image.png