Iterator 遍历器
一、什么是 Iterator 遍历器
Iterator 遍历器是一种接口,基于迭代器模式,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)
Iterator 的作用是为各种数据结构,提供一个统一的的访问接口,使数据结构的成员能够按某种次序排列,从而完成遍历操作。
二、Iterator 的基本用法
ES6 规定,默认的 Iterator 接口部署在数据结构的 Symbol.iterator
属性,一个数据结构只要具有Symbol.iterator
属性,就可以认为是“可遍历的”(iterable)。
// 固定写法
obj[Symbol.iterator] = function () {
// 输出 返回值 (格式要求)
return {
// 必须返回一个 next 方法
next() {
// 必须返回两个值
return {
value: 1, // 当前遍历的项目的值
done: true, // true 表示遍历结束,false 反之
}
}
}
}
上面代码中,对象 obj 是可遍历的(iterable),因为具有 Symbol.iterator
属性,执行这个属性,会返回一个遍历器对象(本质上是一个指针对象),该对象的根本特征就是具有 next 方法,会不断调用 next 方法,直到它指向数据结构的结束位置。
每次调用 next 方法,都会返回一个代表当前成员的信息对象,具有 value 和 done 两个属性。其中,value 属性是当前成员的值,done 属性是一个布尔值,表示遍历是否结束。
示例:
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
上面代码中 arr 是一个数组,对于原生部署 Iterator 接口的数据结构,不用自己写遍历器生成函数,for...of
循环会自动遍历它们,所以调用 Symbol.iterator
可以直接得到遍历器对象。
除此之外,其他数据结构(主要是对象)的 Iterator 接口,都需要自己在 Symbol.iterator
属性上面部署,这样才会被 for...of
循环遍历。
三、Iterator 的应用
1、提取对象中对象数组的值
现在要把下面 authors 中的所有作者名称提取出来:
let authors = {
allAuthors: {
fiction: [
'Agatha Christie',
'J. K. Rowling',
'Dr. Seuss'
],
scienceFiction: [
'Neal Stephenson',
'Arthur Clarke',
'Isaac Asimov',
'Robert Heinlein'
],
fantasy: [
'J. R. R. Tolkien',
'J. K. Rowling',
'Terry Pratchett'
]
}
}
1-1、ES5 的写法
let result = []
for (let [k, v] of Object.entries(authors.allAuthors)) {
result = result.concat(v)
}
console.log(result)
//["Agatha Christie", "J. K. Rowling", "Dr. Seuss", "Neal Stephenson", "Arthur Clarke", "Isaac Asimov", "Robert Heinlein", "J. R. R. Tolkien", "J. K. Rowling", "Terry Pratchett"]
这种方式对 obj 不支持遍历的对象进行改造,需要知道 authors 的内部结构。
优化目标:
- 写法优雅。
- 数据结构种的内置逻辑不需要关心。
最终优化成下面的写法:
let result = []
for (let v of authors) {
result.push(v)
}
1-2、ES6 的写法
// 固定写法
authors[Symbol.iterator] = function () {
// 输入 this,对象本身
let allAuthors = this.allAuthors
let keys = Reflect.ownKeys(allAuthors)
console.log(keys) // ["fiction", "scienceFiction", "fantasy"]
let values = [] // 是 key 的值
return {
// 必须返回一个方法
next() {
console.log(values)
// 一开始 values.length 是 0,如果是 0 就进入循环过程
if (!values.length) {
if (keys.length) {
values = allAuthors[keys[0]]
keys.shift() // 永远取第一个元素,用完之后进行弹出
}
}
// 必须返回两个值
return {
done: !values.length,
value: values.shift()
}
}
}
}
let result = []
for (let v of authors) {
result.push(v)
}
console.log(result)
//["Agatha Christie", "J. K. Rowling", "Dr. Seuss", "Neal Stephenson", "Arthur Clarke", "Isaac Asimov", "Robert Heinlein", "J. R. R. Tolkien", "J. K. Rowling", "Terry Pratchett"]
1-3、与 Generator 结合使用
// 可迭代协议 加 * 就是 Generator 了
authors[Symbol.iterator] = function* () {
// 输入 this,对象本身
let allAuthors = this.allAuthors
let keys = Reflect.ownKeys(allAuthors)
console.log(keys) // ["fiction", "scienceFiction", "fantasy"]
let values = [] // 是 key 的值
// 无限循环,如果退出之后,会自动中止退出的
while (1) {
if (!values.length) {
if (keys.length) {
values = allAuthors[keys[0]]
keys.shift()
yield values.shift()
} else {
// 退出循环
return false
}
} else {
yield values.shift()
}
}
}