前言
在JavaScript中,迭代对象的概念是在ES6(ECMAScript 2015)规范中引入的。ES6引入了迭代器(Iterator)和可迭代对象(Iterable)的概念,以及相关的语法和API。
可迭代对象是一个对象,它有一个[Symbol.iterator]
方法,该方法返回一个迭代器对象。这意味着你可以使用for...of循环来遍历可迭代对象的值。
迭代器
迭代器是借鉴C++等语言的概念,迭代器的原理就像指针一样,它指向数据集合中的某个元素,你可以获取它指向的元素,也可以移动它以获取其它元素。迭代器类似于数组中下标的拓展,各种数据结构,如链表(List)、集合(Set)、映射(Map)
都有与之对应的迭代器。
JS中的迭代器是专门为了遍历这一操作设计的。每次获取到的迭代器总是初始指向第一个元素,并且迭代器只有next()
一种行为,直到获取到数据集的最后一个元素。我们无法灵活移动迭代器的位置,所以迭代器的任务,是按某种次序遍历数据集中的元素。
迭代器是一个对象,它提供了一种遍历给定数据结构的方法。迭代器对象有一个next()
方法,每次调用该方法时,它返回一个包含两个属性的对象:value
和done
。value
属性包含当前迭代到的值,而done
属性是一个布尔值,表示是否还有更多的值可以迭代。
调用数组的迭代器
先认识下数组的迭代器(Symbol.unscopables)它存在于数组的原型上,如下图:

直接调用Symbol.iterator属性就可以调用数组的迭代器。
js
let arr = ["a", "b"]
let arrIterator = arr[Symbol.iterator]()
console.log(arrIterator.next()) // {value: 'a', done: false}
console.log(arrIterator.next()) // {value: 'b', done: false}
console.log(arrIterator.next()) // {value: undefined, done: true}
上面我们也提到了可迭代对象的特点是有一个[Symbol.iterator]
方法,拥有该方法的对象是可以被for...of...遍历的。那么哪些数据结构可以被for...of..遍历呢?
常见的有以下几种:Array、String、Set 和 Map,特殊类数组对象:arguments、NodeList 等
,下面以Set和Map数据结构为例:
1. Set()数据结构
js
const set = new Set([1, 2, 3, 4, 4])
for (const value of set) {
console.log(value) // 1 2 3 4
}
2. Map()数据结构
js
const map = new Map([
["name", "张三"],
[
{
a: "Hello World",
},
"你好",
],
])
for (let [name, value] of map) {
console.log("name:", name) // name: name // name: {a: 'Hello World'}
console.log("value:", value) // value: 张三 // value: 你好
}
为类数组添加迭代器
在JavaScript中,"类数组"(array-like)对象是指那些具有数字索引的属性和长度属性,但没有prototype.push
、prototype.pop
、prototype.shift
、prototype.unshift
、prototype.splice
、prototype.slice
、prototype.concat
、prototype.join
等方法可调用的对象。类数组对象最常见的例子是字符串和NodeList对象。
类数组对象并不是真正的数组,但它们具有与数组相似的特性,例如索引属性和长度属性。由于类数组对象没有数组的方法,因此不能直接使用数组的方法来处理它们。
使用for...of...遍历类数组
js
const arrayLike = {
0: "a",
1: "b",
2: "c",
length: 3,
}
for (let value of arrayLike) {
console.log(value) // Uncaught TypeError: arrayLike is not iterable
}
🙅显然是不行滴!!!所以需要添加迭代器。
js
const arrayLike = {
0: "a",
1: "b",
2: "c",
length: 3,
}
arrayLike.__proto__[Symbol.iterator] = function () {
let index = 0
return {
next: () => {
return this.length > index
? {value: this[index++], done: false}
: {value: undefined, done: true}
},
}
}
for (let value of arrayLike) {
console.log(value) // a b c
}
以上是为类数组添加迭代器的方法,如果你没有看懂,建议仔细阅读迭代器的概念。
既然类数组可以添加迭代器,那么普通对象能不能添加迭代器呢?
普通对象添加迭代器
分析:普通对象并不像类数组那样对象的key是一堆有序的数字,并且也没有长度,所以在为普通对象添加迭代器的时候先解决这两个问题,不妨我们试试先遍历对象。
js
let man = {
name: "小明",
age: 20,
}
man.__proto__[Symbol.iterator] = function () {
let keys = []
let index = 0
for (let key in this) {
// 筛选自身属性
if (this.hasOwnProperty(key)) {
keys.push(key)
}
}
return {
next: () => {
return keys.length > index
? {value: this[keys[index++]], done: false}
: {value: undefined, done: true}
},
}
}
for (let v of man) {
console.log(v) // 小明 20
}
如果为类数组添加迭代器已经看懂,相信为普通对象添加迭代器你也能看懂💪
后记
创作是一个复杂的过程,需要将思考、灵感和技巧完美结合,每一次的成功都来之不易。以上是我花费了N个小时呕心沥血的整理出来的。感谢观看、点赞、转发、收藏 ⛽️