Iterator 概念
遍历器是一种接口,为各种不同的数据结构提供统一的访问机制。
作用:
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使数据结构成员能够按某种次序排列
- 主要供 for…of 消费
遍历过程:
- 创建一个指针对象
- 第一次调用指针对象的next方法,指针指向数据结构的第一个成员
- 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员
- 不断调用指针对象的next方法,知道它指向数据结构的结束位置
每次调用next方法,返回一个包含value 和 done 两个属性的对象。
默认Iterator 接口
一种数据结构只要部署了 Iterator 接口,就是可遍历的。
Es6,默认的Iterator 接口部署在数据结构的Symbol.iterator 属性。
原生具备Iterator接口的数据结构:
- -Array
- -Map
- -Set
- -String
- -TypedArray
- -函数的arguments对象
- -NodeList对象
调用Iterator 接口的场合
以下场景会默认调用Iterator接口(即Symbol.iterator方法)
解构赋值
对数组和Set结构赋值时,会默认调用Symbole.iterator 方法
let set = new Set()
set.add(1)
set.add(2)
set.add(3)
let [x, y] = set
扩展运算符
扩展运算符也会调用默认的 Iterator 接口
let str = 'hello'
[...str]
yield*
yield* 后面跟一个可遍历的结构,它会调用该结构的遍历器接口
let generator = function* () {
yield 1;
yield* [2, 3, 4];
yield 5;
}
let generatorIterator = generator()
console.log(generatorIterator.next())
console.log(generatorIterator.next())
console.log(generatorIterator.next())
console.log(generatorIterator.next())
console.log(generatorIterator.next())
其他场合
由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,都可以调用遍历器接口。
-for…of
-Array.from()
-Map(), Set(), WeakMap(), WeakSet()
-Promise.all()
-Promise.race()
字符串的Iterator接口
字符串是一个类似数组的对象,也原生具有Iterator 接口。
var someString = 'hi'
var strIter = someString[Symbol.iterator]()
console.log(strIter.next())
console.log(strIter.next())
console.log(strIter.next())
可以覆盖原生的Symbol.iterator 方法,达到修改遍历器行为的目的。
var objStr = new String('hello')
console.log([...objStr])
objStr[Symbol.iterator] = function() {
return {
next: function() {
if (this._first) {
this._first = false
return { value: 'bye', done: false }
} else {
return { done: true }
}
},
_first: true
}
}
console.log([...objStr]) // ['bye']
console.log(objStr) // {'hello'}
Iterator 接口 与 Generator 函数
let myIterable = {
* [Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
yield 4;
}
}
console.log([...myIterable])
遍历器对象的 return(), throw()
for…of 循环
数组
const arr = ['red', 'green', 'blue']
for (let v of arr) {
console.log(v)
}
// 遍历数组索引
for (let key of Object.keys(arr)) {
console.log(key)
}
for (let [key, value] of Ojbect.entries(arr)) {
console.log(key, value)
}
Set 和 Map 结构
Set 和 Map 结构也原声具有Iterator 接口
let engines = new Set(['Gecko', 'Trident', 'Webkit', 'webkit'])
for (let e of engines) {
console.log(e)
}
let es6 = new Map()
es6.set('edition', 6)
es6.set('committee', 'Tc39')
for (let [key, value] of es6) {
console.log(key + ' : ' + value)
}
对象
for (let key of Object.keys(somObject)) {
console.log(key+': ' + someObject[key])
}
// 使用Generator 函数将对象重新包装一下
function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, Obj[key]]
}
}
for (let [key, value] of entries(obj)) {
console.log(key, '-> ', value)
}
与其他遍历语法的比较
// for 循环
for (let index = 0; index < myArray.length; index++) {
console.log(myArray[index])
}
// forEach 方法
myArray.forEach((value) => {
console.log(value)
})
--forEach, 无法中途跳出forEach 循环, break和return 都不能奏效
// for...in 循环
for (let index in myArray) {
console.log(myArray[index])
}
-----for...in缺点,不适用于遍历数组
-数组的键名是数字
-还会遍历手动添加的其他键,包括原型链上的键
-某些情况下,for...in循环会以任意顺序遍历键名
// for...of 循环优点:
let iteArr = [10, 30, 60, 70]
for (let n of iteArr) {
if (n > 50) {
break
}
console.log(n)
}
-有着同for...in 一样简洁语法,但是没有for...in那些缺点
-不用于forEach,它可以与break、continue和return配合使用
-提供了遍历所有数据结构的统一操作接口