1. 重点提炼
- 迭代器Iterator的使用与介绍
- 自定义遍历
2. 迭代器Iterator
- 是一种接口机制,为各种不同的数据结构提供统一访问的机制,即
Iterator
就是ES6
中用来实现自定义遍历的接口 - 主要供
for…of
消费 - 一句话:不支持遍历的数据结构“可遍历”
该接口有硬性要求,必须return
一个对象,并且有一个next
函数。next
函数也需要返回一个对象,对象中包含value
和done
属性值。
完成一个遍历器生成的函数,作用则是返回遍历器对象。
首先创建一个类似指针的索引变量,指向当前数组的初始位置,实际遍历器的本质就是指针对象。 => 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())
参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.20
Branch: branch02commit 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]
方法。
参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.21
Branch: branch02commit 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
方法的。
参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.22
Branch: branch02commit 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())
参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.23
Branch: branch02commit 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
函数返回一个对象,对象中包括value
和done
属性。
- 首先,它是一个对象
- 其次,这个对象包含一个无参函数
next
- 最后,
next
返回一个对象,对象包含done
和value
属性。其中done
表示遍历是否结束,value
返回当前遍历的值。
WARNING
如果
next
函数返回一个非对象值(比如false
和undefined
) 会展示一个TypeError ("iterator.next() returned a non-object value")
的错误
如果让一个对象是可遍历的,就要遵守可迭代协议,该协议要求对象要部署一个以 Symbol.iterator
为 key
的键值对,而 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)
}
参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.24
Branch: branch02commit 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
方法和包含 done
、value
属性的返回对象)。
参考:https://github.com/6xiaoDi/blog-ECMScript-Series/tree/a3.25
Branch: branch02commit description:a3.25(迭代器Iterator——Generator自定义结构可遍历)
tag:a3.25
实际上任何一种结构都是的可遍历的。
(后续待补充)