携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
前言
迭代器在日常开发中几乎达到了不可或缺的地步,极大的方便了开发者遍历各种数据。
但并不是所有的数据类型都有迭代器,作为开发最常用的数据结构之一的 Object,就没有自身的迭代器,每次遍历,都不得不写上相同的遍历代码。写多了,自然就会觉得繁琐,同时,也无法享受到 for...of 循环和 扩展运算符(...)的简洁写法。
那么,能不能给 Object 也添加一个迭代器?
迭代协议
本文就简单的总结一下迭代协议的主要内容:
- 可迭代对象必须有一个
@@iterator的方法,不管这个方法是在对象本身还是在其原型链上都行;@@iterator方法的属性名称是Symbol.iterator,内容是一个函数,返回一个迭代器;- 迭代器是一个对象,对象中必须存在一个
next方法;next方法每次返回一部分迭代的数据,返回的数据结构为{ done, value },dnoe表示是否已经迭代完毕,value为当前的返回值。
每一次迭代时,都会调用 @@iterator 方法生成一个迭代器 iterator,然后通过 iterator.next() 逐个获取可迭代对象的值。
代码
首先,定义测试的数据:
const myInfo = {
name: 'white bear',
age: 18
}
在没有迭代器的情况下,直接使用 for...of 循环和 扩展运算符(...)会直接报错:
console.log('myInfo:', ...myInfo)
// Uncaught TypeError: Found non-callable @@iterator
for (let item of myInfo) {
console.log('item:', item)
}
// Uncaught TypeError: myInfo is not iterable
两个错误都是因为 Object 上不存在 @@iterator 方法,也就被判定为不可迭代。
那就给 Object 加上 @@iterator 方法,过程思路和代码如下:
// 直接将 @@iterator 方法,添加在 Object 的原型上
// 注意,不能使用 箭头函数,箭头函数没有自己的 this ,无法拿到对象数据
Object.prototype[Symbol.iterator] = function () {
// this 指向当前调用的对象,使用一个变量保存起来
const obj = this
// 获取所有的对象的键
const keys = Object.keys(obj)
// 索引,记录当前对象的键的取值
let index = 0
// 返回迭代器,这也是一个对象,但是这个对象必须拥有 next 方法
return {
// 定义 next 方法
next () {
const key = keys[index]
// 设定value的值, 此处是一个 key 和对应的值组成的对象
const value = { key, value: obj[key] }
// 索引指向下一个键
index++
// next 的返回值必须拥有的 done 和 value
// done 表示是否已经遍历完成,此处利用 key 是否存在作为判断
// value 为当前的返回值
return { done: key === undefined, value }
}
}
}
然后,再次使用 for...of 循环和 扩展运算符(...):
console.log('myInfo:', ...myInfo)
// myInfo: {key: 'name', value: 'white bear'} {key: 'age', value: 18}
for (let item of myInfo) {
console.log('item:', item)
}
// item: {key: 'name', value: 'white bear'}
// item: {key: 'age', value: 18}
至此,一个 Object 的迭代器基本上就完成了,至于迭代器每次返回的内容,可以根据开发需求进行自定义修改。