Iterator 的实现原理

682 阅读2分钟

Iterator 是什么

iterator是遍历数据结构(Array,类Array,Set,Map等)提供的统一接口。

刚接触的小伙伴肯定和我当初一样,对这概念一脸懵逼。我们先看一下它大概长什么样

let itrator = {
    next(){
        return {value,done}  //value是每次返回的值,done是遍历有没有结束
    }
}

那这有什么用呢?我们可以在array,set,map等数据结构中实现类似这样的iterator 接口,通过统一的方法的遍历他们,例如:for of。

我们先试着实现一个数组的iterator

        Array.prototype.iterator = function(){
            let i = 0
            return {
                next:()=>{
                    let ret = this.length>i?{value:this[i],done:false}:{value:undefined,done:true}
                    i++
                    return ret
                 }
            }
        }
        let arr = [1,2,3]
        let iterator = arr.iterator()        
        //遍历
        let mark = true
        while(mark){
            let {value,done} = iterator.next()
            console.log(value,done);
            mark = !done
        }

手动遍历的方式也太傻了点吧,赶紧换成es6提供for of方式来尝尝鲜。源码中返回iterator的方法的属性名是[Symbol.iterator],它是一个Symbol类型。

        Array.prototype[Symbol.iterator] = function(){
            let i = 0
            return {
                next:()=>{
                    let ret = this.length>i?{value:this[i],done:false}:{value:undefined,done:true}
                    i++
                    return ret
                 }
            }
        }
        let arr = [1,2,3]
        for(let val of arr){
            console.log(val);
        }

到这,我们发现只要实现了iterator接口的数据我们都可以用for of遍历了,字符串、map、set等都实现了各自不同的iterator,感兴趣的自己实现下

实现Iterator 接口的数据结构

es6中实现Iterator 接口的数据结构如下

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

既然这样的话,都已经帮我们实现好了,那我们何不干点坏事、拿到它默认的提供的iterator,我们手动遍历它

        let arr = [1,2,3]
        let iterator = arr[Symbol.iterator]()

        let mark = true
        while(mark){
            let {value,done} = iterator.next()
            console.log(value,done);
            mark = !done
        }

实现一个对象的Iterator

es6中为那多数据结构提供的iterator,为什么就没有对象呢?因为对象的遍历顺序不能确定,需要我们手动指定,es6也给我们提供map这样的有序数据结构。

不给我们提供,那我们自己实现呗

第一种:常规手段

        Object.prototype[Symbol.iterator] = function () {
            let properties = Reflect.ownKeys(this)   //获取普通属性和symbol,map中的symbol属性也可以遍历
            let i = 0
            return {
                next: () => {
                    let ret = properties.length > i ? { value: [properties[i], this[properties[i]]], done: false } : { value: undefined, done: true }
                    i++
                    return ret
                }
            }
        }
        let obj = {a:1,b:2}
        for(let val of obj){
            console.log(val);
        }

第二种:借助generator函数

因为generator函数返回的是一个遍历器,需要我们通过next一层层调用,每次调用next返回的value就是yield后执行的结果。

        Object.prototype[Symbol.iterator] = function*(){
            for(let key in this){
                yield [key,this[key]]
            }
        }
        let obj = {a:1,b:2}
        for(let val of obj){
            console.log(val);
        }

言简意赅,恰到好处,这就是代码的魅力

如果对generator函数感兴趣,可以看下我们的另一篇文章juejin.cn/post/684490…