11.(ECMAScript)es6完全解读(10)

1. 重点提炼

  • 迭代器Iterator的使用与介绍
  • 自定义遍历

2. 迭代器Iterator

  • 是一种接口机制,为各种不同的数据结构提供统一访问的机制,即Iterator就是 ES6中用来实现自定义遍历的接口
  • 主要供for…of消费
  • 一句话:不支持遍历的数据结构“可遍历”

该接口有硬性要求,必须return一个对象,并且有一个next函数。next函数也需要返回一个对象,对象中包含valuedone属性值。

完成一个遍历器生成的函数,作用则是返回遍历器对象。

首先创建一个类似指针的索引变量,指向当前数组的初始位置,实际遍历器的本质就是指针对象。 => nextIndex

第一次调用next,就相当于指向当前数据结构的第一个成员,然后依次类推。(会发现非常类似generator

function makeIterator(arr) {
    let nextIndex = 0
    return {
        next() {
            return nextIndex < arr.length ? {
                value: arr[nextIndex++],
                done: false
            } : {
                value: undefined,
                done: true
            }
        }
    }
}
let it = makeIterator(['a', 'b', 'c'])
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

image-20201127101733522

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.20
Branch: branch02

commit description:a3.20(迭代器Iterator——遍历器生成的函数)

tag:a3.20


不支持遍历的数据结构“可遍历”

let courses = {
    allCourse: {
        frontend: ['ES', '小程序', 'Vue', 'React'],
        backend: ['Java', 'Python', 'SpringBoot'],
        webapp: ['Android', 'IOS']
    }
}

for(let c of courses){
    console.log(c)
}

报错了,提示迭代了不可迭代的实例。为了可迭代,非数组对象必须有一个[Symbol.iterator]方法。

image-20201127102453911

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.21
Branch: branch02

commit description:a3.21(迭代器Iterator——对象遍历报错)

tag:a3.21


如何让结构变为可迭代的?

浏览一个可迭代的数据结构 =>

调用它的Symbol.iterator方法 => arr[Symbol.iterator]()

let arr = ['a', 'b', 'c']
console.log(arr)
let it = arr[Symbol.iterator]()
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

浏览数组的原型,它是有Symbol.iterator方法的。

image-20201127112016491

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.22
Branch: branch02

commit description:a3.22(迭代器Iterator——数组的原型,它是有Symbol.iterator方法的)

tag:a3.22


map也是包含Symbol.iterator方法的

let map = new Map()
map.set('name', 'es')
map.set('age', 5)
map.set('school', 'QH')
console.log(map)
let it = map[Symbol.iterator]()
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

image-20201127112516756

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.23
Branch: branch02

commit description:a3.23(迭代器Iterator——map的原型,它是有Symbol.iterator方法的)

tag:a3.23


3. 原生具备Iterator接口的数据结构

  • Array
  • Map
  • Set
  • String
  • TypedArray => 底层二进制数据进行描述,平时用得少
  • 函数的arguments对象 => 类数组
  • NodeList对象 => dom操作返回的类数组

4. 自定义结构可遍历

可迭代协议 => 当前对象上是否有[Symbol.iterator]属性,即这个对象(或者它原型链 prototype chain上的某个对象)必须有一个名字是 Symbol.iterator的属性,代表可迭代,可用for…of遍历。

迭代器协议 => 当前的迭代器必须符合条件:返回一个对象,对象中包含next函数,next函数返回一个对象,对象中包括valuedone属性。

  • 首先,它是一个对象
  • 其次,这个对象包含一个无参函数 next
  • 最后,next返回一个对象,对象包含 donevalue属性。其中 done 表示遍历是否结束,value返回当前遍历的值。

WARNING

如果 next函数返回一个非对象值(比如falseundefined) 会展示一个 TypeError ("iterator.next() returned a non-object value")的错误

如果让一个对象是可遍历的,就要遵守可迭代协议,该协议要求对象要部署一个以 Symbol.iteratorkey的键值对,而 value就是一个无参函数,这个函数返回的对象要遵守迭代器协议。

let courses = {
    allCourse: {
        frontend: ['ES', '小程序', 'Vue', 'React'],
        backend: ['Java', 'Python', 'SpringBoot'],
        webapp: ['Android', 'IOS']
    }
}

courses[Symbol.iterator] = function () {
    let allCourse = this.allCourse
    let keys = Reflect.ownKeys(allCourse)
    let values = []
    return {
        next() {
            if (!values.length) {
                if (keys.length) {
                    values = allCourse[keys[0]]
                    keys.shift()
                }
            }
            return {
                done: !values.length,
                value: values.shift()
            }
        }
    }
}

for (let c of courses) {
    console.log(c)
}

image-20201127114516385

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.24
Branch: branch02

commit description:a3.24(迭代器Iterator——自定义结构可遍历)

tag:a3.24

以上写起来比较麻烦,直接遍历一个个取,但是如果存在一个很复杂的数据结构,很多模块都要用,如果把它写成一个可迭代的,每个模块迭代的时候,直接for…of,就不用各个模块自己实现迭代逻辑了,这样后期代码也不好维护。


实际上Generator也是返回一个对象,和迭代器一样,它是天然满足可迭代协议的。。因此可以直接iterator迭代器中,直接使用Generator的,就不用自己定义next方法了。

let courses = {
    allCourse: {
        frontend: ['ES', '小程序', 'Vue', 'React'],
        backend: ['Java', 'Python', 'SpringBoot'],
        webapp: ['Android', 'IOS']
    }
}

courses[Symbol.iterator] = function* () {
    let allCourse = this.allCourse
    let keys = Reflect.ownKeys(allCourse)
    let values = []
    while (1) {
        if (!values.length) {
            if (keys.length) {
                values = allCourse[keys[0]]
                keys.shift()
                yield values.shift()
            } else {
                // 遍历完毕后,退出
                return false
            }
        }else{
            yield values.shift()
        }
    }
}

for (let c of courses) {
    console.log(c)
}

同一个场景,同一个数据结构,写法确实不同的,利用 Generator就不再需要显示的写迭代协议了(next方法和包含 donevalue属性的返回对象)。

image-20201127114516385

参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.25
Branch: branch02

commit description:a3.25(迭代器Iterator——Generator自定义结构可遍历)

tag:a3.25

实际上任何一种结构都是的可遍历的。




(后续待补充)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值