迭代器
介绍
迭代器是一种设计模式,通过可迭代对象的提供迭代接口获取迭代对象,依次执行迭代对象提供的方法输出值
可迭代对象其特征如下:
- 有序性:元素具有明确的顺序
- 连续性:元素可以按顺序连续访问
以下类型是可迭代对象
- Array(数组)
- Map(键值对映射)
- Set(集合)
- NodeList(DOM 节点列表)
- TypedArray(类型化数组)
- arguments(函数的参数对象)
Symbol.iterator
在可迭代对象上存在Symbol.iterator方法
,调用Symbol.iterator方法
可以得到一个迭代器*,在通过调用迭代器next()
方法可以得到一个迭代结果,包含两个属性:
迭代结果是一个对象,包含两个属性:
value
:当前迭代到的值done
:布尔值,表示迭代是否结束
// 定义可迭代对象
const arr = [1, 2, 3]
// 获取迭代对象
const arrIterator = arr[Symbol.iterator]()
// 执行迭代器
arrIterator.next() // {value: 1, done: false}
arrIterator.next() // {value: 2, done: false}
arrIterator.next() // {value: 3, done: false}
arrIterator.next() // {value: undefined, done: true}
实现原理
以下是实现一个类似Symbol.iterator方法
Symbol.iterator = Symbol('Symbol.iterator')
Object.defineProperty(Array.prototype, Symbol.iterator, {
configurable: false,
enumerable: false,
writable: false,
value() {
let index = 0
const data = this
return {
next() {
if (index < data.length) {
return {
value: data[index++],
done: false,
}
}
else {
return {
value: undefined,
}
}
},
}
},
})
不同的迭代器
每个可迭代对象不仅提供Symbol.iterator方法
,还提供了其他方法(如 keys
、values
和 entries
),这些方法也返回迭代器对象
这些方法的具体功能如下:
- keys():返回一个迭代器,输出数组的键(索引)。
- values():返回一个迭代器,输出数组的值。
- entries():返回一个迭代器,输出数组的键值对(索引和值的组合)。
// 定义可迭代对象
const arr = [1, 2, 3]
// 获取迭代器对象
const arrIterator = arr[Symbol.iterator]()
const arrKeysIterator = arr.keys()
const arrValuesIterator = arr.values()
const arrEntriesIterator = arr.entries()
// 执行迭代器
arrIterator.next() // { value: 1, done: false }
arrKeysIterator.next() // { value: 0, done: false }
arrValuesIterator.next() // { value: 1, done: false }
arrEntriesIterator.next() // { value: [ 0, 1 ], done: false }
在可迭代对象中,Symbol.iterator方法
实际上是 keys
、values
或 entries
方法中的其中之一
对象不迭代性
// 定义可迭代对象
const obj = { a: 1, b: 2, c: 3 }
// 获取迭代器对象
const objIterator = obj[Symbol.iterator]() // obj[Symbol.iterator] is not a function
// 输出Object的迭代器接口
Object.prototype[Symbol.iterator] // undefined
obj对象
没有迭代器接口,也就无法生成迭代器对象
注:这是因为Object对象
的属性没有明确的顺序
如果需要使Object对象
可迭代,可以通过手动实现Symbol.iterator方法
来实现
// 定义可迭代对象
const obj = { a: 1, b: 2, c: 3 }
obj[Symbol.iterator] = function () {
const keys = Object.keys(this)
let index = 0
return {
next: () => {
if (index < keys.length) {
return {
value: {
key: keys[index],
value: this[keys[index++]],
},
done: false,
}
}
else {
return {
value: undefined,
done: true,
}
}
},
}
}
// 获取迭代器对象
const objIterator = obj[Symbol.iterator]()
// 执行迭代器
objIterator.next() // { value: { key: 'a', value: 1 }, done: false }
objIterator.next() // { value: { key: 'b', value: 2 }, done: false }
objIterator.next() // { value: { key: 'c', value: 3 }, done: false }
objIterator.next() // { value: undefined, done: true }
使用
手动迭代
这种方式并不常用,因为手动执行迭代器的次数是未知的,且需要手动检查 done 属性以判断迭代是否完成。
// 定义可迭代对象
const set = new Set([1, 2, 3])
// 获取迭代对象
const setIterator = set[Symbol.iterator]()
// 执行迭代器
console.log(setIterator.next()) // {value: 1, done: false}
循环迭代
循环迭代避免了迭代器对象执行执行次数未知的问题,但是任然需要手动判断迭代是否结束
// 定义可迭代对象
const set = new Set([1, 2, 3])
// 获取迭代对象
const setIterator = set[Symbol.iterator]()
// 执行迭代器
while (true) {
const { value, done } = setIterator.next()
if (done)
break
console.log(value)
}
在ES6中,提供了for...of循环,会自动调用被循环的对象的Symbol.iterator方法
返回一个迭代器对象,然后依次调用迭代器对象的next方法
参与循环体的循环
// 定义可迭代对象
const set = new Set([1, 2, 3]);
// 使用循环迭代
for (const item of set) {
console.log(item);
}
扩展运算符
这也是ES6新增的语法,使用扩展运算符...
可以展开一个可迭代对象,返回一个数组
内部是通过调用Symbol.iterator方法
获取被展开对象的迭代器,后依次调用被展开对象的next方法
输出展开对象中的值数组
const arr = [1, 2, 3]
const copyArr2 = [...arr]