JS 中表示“集合”的数据结构有:Array、Object、Map、Set等。
需求:需要统一的接口机制,遍历不同表示“集合”的数据结构。
解决方案:遍历器(Iterator)就是这个接口,针对不同的数据结构完成都可遍历。
一般项目里其实不太用使用Iterator
,但是理解这个,可能是理解其他的基础,比如生成器。
Iterator 的遍历过程
遍历器对象本质上,就是一个指针对象(联想遍历各种结构的指针)。
(1)创建一个指针对象,指向当前数据结构的起始位置。,
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
怎么添加 Iterator接口
其实就是在数据结构中增加一个Symbol.iterator
属性,其是一个返回**Iterator(遍历器)**的函数。
遍历器的本质是个对象,有个next
方法(next的本质是函数,所以能调用),其是一个返回{value:xx,done:true/false}
的函数。
value
属性返回当前位置的成员,done
属性是一个布尔值,表示是否遍历结束,即是否还有必要再一次调用next方法。
用for of
遍历数据结构,就可以得到每次的value
。
var obj = {
[Symbol.iterator]: function () {
const keys = Object.keys(this)
let p = 0
return { next: () => {
const res = { value: this[keys[p]], done: keys.length < p + 1 }
p++
return res
} }
},
a: 1,
b: 2
}
let it = obj[Symbol.iterator]()
// 所谓的迭代器就是一个对象
console.log(it) // { next: [Function: next] }
console.log(it.next()) // { value: 1, done: false }
console.log(it.next()) // { value: 2, done: false }
console.log(it.next()) // { value: undefined, done: true }
for (let v of obj) {
// 和上面一样,输出两次,1、2
console.log(v)
}
复制代码
也可使用生成器添加 Iterator接口
生成器执行的时候,直接返回遍历器实例。
var obj = {
[Symbol.iterator]: function * () {
yield 1
yield 2
},
a: 1,
b: 2
}
let it = obj[Symbol.iterator]()
console.log(it) // Object [Generator] {}
console.log(it.next()) // { value: 1, done: false }
console.log(it.next()) // { value: 2, done: false }
console.log(it.next()) // { value: undefined, done: true }
for (let v of obj) {
// 和上面一样,输出两次,1、2
console.log(v)
}
复制代码
天生有Iterator接口的数据结构
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
注意!!!对象没有内置Iterator接口,所以如果不是手动添加,不能使用for of
遍历
用数组举例看下:
var arr = [1, 2]
let it = arr[Symbol.iterator]()
console.log(it) // { next: [Function: next] }
console.log(it.next()) // { value: 1, done: false }
console.log(it.next()) // { value: 2, done: false }
console.log(it.next()) // { value: undefined, done: true }
for (let v of arr) {
// 和上面一样,输出两次,1、2
console.log(v)
}
复制代码
遍历器的使用场景
- 解构
- 扩展运算符
- 由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口
- for...of
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
- Promise.all()
- Promise.race()
和其他遍历语法的比较
主要以遍历数组为例:
for
:比较繁琐forEach
:无法中途跳出forEach循环for in
:遍历数组的键名(索引),还有原型链上的键。当然一般遍历对象用的
for of
的优点:
- 有着同
for...in
一样的简洁语法,但输出的是是当前键对应的值。 - 可以
break、continue和return
配合使用。 - 提供了遍历所有数据结构的统一操作接口。
注意对象不能直接使用for of
,因为内置没有Iterator
。