ES6如何让不支持遍历的数据结构“可遍历”?
1、简单数据结构的同类数据聚合
// 简单数据结构的同类数据聚合
// 业务场景:聚合不同图书类型的作者
let authors = {
allAuthors: {
fiction: ['Agla', 'Skks', 'LP'],
scienceFiction: ['Neal', 'Mike', 'Ribert'],
fantasy: ['J.R.Tole', 'J.M.R', 'Terry P.K']
},
Addres: []
}
let r = []
for (let [k, v] of Object.entries(authors.allAuthors)) { // 转换成可遍历对象
r = r.concat(v)
}
console.log(r) // ["Agla", "Skks", "LP", "Neal", "Mike", "Ribert", "J.R.Tole", "J.M.R", "Terry P.K"]
Iterator自定义遍历器语法解读:在对象上挂载一个 Symbol.iterator,赋值给一个方法,这个方法的输入是this也就是对象本身,输出是一个对象,对象上必须有一个next方法,且next方法必须返回一个对象,对象有两个字段done和value
let authors = {
allAuthors: {
fiction: ['Agla', 'Skks', 'LP'],
scienceFiction: ['Neal', 'Mike', 'Ribert'],
fantasy: ['J.R.Tole', 'J.M.R', 'Terry P.K']
},
Addres: []
}
authors[Symbol.iterator] = function () {
return { // 在自定义接口中返回一个对象,这个对象有一个next()方法
next() {
return { // next()方法返回一个对象,对象必须包含两个字段 done value
done: false, // false 遍历没有结束 true 遍历已结束
value: 1 // 当前被遍历项的值
}
}
}
}
1. 迭代器协议
属性 | 值 | 必选 |
---|---|---|
next | 返回一个对象的无参函数,被返回对象拥有两个属性:done 和 value | Y |
这是两个概念:可迭代协议、迭代器协议。通俗的讲,迭代器协议要求符合以下条件:
- 首先,它是一个对象
- 其次,这个对象包含一个无参函数 next
- 最后,next 返回一个对象,对象包含 done 和 value 属性。其中 done 表示遍历是否结束,value 返回当前遍历的值。
[!DANGER]
如果 next 函数返回一个非对象值(比如false和undefined) 会展示一个 TypeError (“iterator.next() returned a non-object value”) 的错误
2. 可迭代协议
可迭代协议允许 JavaScript 对象去定义或定制它们的迭代行为, 例如(定义)在一个 for…of 结构中什么值可以被循环(得到)。一些内置类型都是内置的可迭代类型并且有默认的迭代行为, 比如 Array or Map, 另一些类型则不是 (比如Object) 。
为了变成可迭代对象, 一个对象必须实现 @@iterator 方法, 意思是这个对象(或者它原型链 prototype chain 上的某个对象)必须有一个名字是 Symbol.iterator 的属性:
属性 | 值 | 必选 |
---|---|---|
[Symbol.iterator] | 返回一个对象的无参函数,被返回对象符合迭代器协议 | Y |
如果让一个对象是可遍历的,就要遵守可迭代协议,该协议要求对象要部署一个以 Symbol.iterator 为 key 的键值对,而 value 就是一个无参函数,这个函数返回的对象要遵守迭代器协议。
authors[Symbol.iterator] = function () {
let allAuthors = this.allAuthors // 输入用this来访问,指对象本身
let keys = Reflect.ownKeys(allAuthors)
let values = []
return { // 在自定义接口中返回一个对象,这个对象有一个next()方法
next() {
if (!values.length) {
if (keys.length) {
values = allAuthors[keys[0]]
keys.shift()
}
}
return { // next()方法返回一个对象,对象必须包含两个字段 done value
done: !values.length, // false 遍历没有结束 true 遍历已结束
value: values.shift() // 当前被遍历项的值
}
}
}
}
let r = []
for (let v of authors) {
r.push(v)
}
console.log(r) // ["Agla", "Skks", "LP", "Neal", "Mike", "Ribert", "J.R.Tole", "J.M.R", "Terry P.K"]
结合Generator实现
authors[Symbol.iterator] = function* () {
let allAuthors = this.allAuthors
let keys = Reflect.ownKeys(allAuthors)
let values = []
while (1) {
if (!values.length) {
if (keys.length) {
values = allAuthors[keys[0]]
keys.shift()
yield values.shift()
} else {
return false
}
} else {
yield values.shift()
}
}
}
let r = []
for (let v of authors) {
r.push(v)
}
console.log(r) // ["Agla", "Skks", "LP", "Neal", "Mike", "Ribert", "J.R.Tole", "J.M.R", "Terry P.K"]