如何使用 for...of 循环遍历对象
引题
今天在群里碰到一个题目,正好最近在看阮一峰的 ES6,所以记录一下:
定义一个 class 类 Book,传入一个数组后可以用 for...of...遍历,即
class Book {
// TODO
}
const book = new Book(['lonely', 'happy', 'rich'])
for (let item of book) {
console.log(item) // 依次输出'lonely', 'happy', 'rich'
}
先说一下最终答案:
class Book {
constructor(book = []) {
this.data = book
}
[Symbol.iterator]() {
let index = 0
return {
next: () => {
return {
value: this.data[index++],
done: index > this.data.length
}
}
}
}
}
提到for...of循环,就得提到遍历器-Iterator
Iterator
遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署了 Iterator 接口,就卡爷呢完成遍历操作。
它的作用主要由三个:
-
提供统一的访问接口
-
使数据结构的成员能够按照次序排列
-
ES6 新增了新的遍历命令
for...of循环,Iterator 接口主要供for...of消费
它的遍历过程是这样的:
-
创建一个指针对象,指向当前数据结构的起始位置,也就是说,遍历器对象本质就是一个指针对象
-
第一次调用指针对象的 next 方法,可以将指针指向数据结构的第一个成员
-
第二次调用指针对象的 next 方法,就指向第二个成员
-
不断调用 next 方法,直到指向数据结束为止
每次调用 next 方法都会返回一个包含value和done两个属性的对象,其中value属性是当前成员的值,done是一个布尔值,代表遍历是否结束。
默认 Iterator 接口
ES6 规定,默认的 Iterator 接口部署在数据结构的 Symbol.iterator 属性,即一个数据结构只要有 Symbol.iterator 属性就代表是可遍历的。Symbol.iterator 属性本身是一个函数,执行这个函数会返回一个遍历器。
一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是可遍历的[Iterable]
ES6 中有些数据结构原生具备 Iterator 接口
-
Array
-
Map
-
Set
-
String
-
TypedArray
-
函数的 arguments 对象
-
NodeList 对象
对象 Object 则没有部署,因为对象的属性遍历顺序是不确定的,想要实现 Object 被for...of循环调用,则可以在原型上添加遍历器。
Object.prototype[Symbol.iterator] = function () {
let index = 0
const keys = Object.keys(this)
return {
next: () => {
return {
value: this[keys[index++]],
done: index > keys.length
}
}
}
}