小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
for...of
我们都知道for...of是ES6中添加的一个循环迭代的方法,用于遍历数组等,节省我们的代码工作量
let arr = [1, 2, 3, 4, 5, 6]
let obj = {
a: '1',
b: '2',
c: '3'
}
for (let val of arr) {
console.log(val)
}
// 1,2,3,4,5,6
for (let val of obj) {
console.log(val)
}
//TypeError: obj is not iterable
从上面我们可以看到,当使用for...of
遍历Object
的时候会报错,告诉我们obj
不是一个iterable
,什么是iterable
,翻译过来叫做可迭代的,我们来看for...of
在MDN
上的定义
for...of
语句在可迭代对象(包括Array
,Map
,Set
,String
,TypedArray
,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
现在我们知道了,因为Object
不是可迭代对象,所以for...of
对它不好用,不能进行正常的遍历
那么为什么数组这一类可以,对象不行呢?它们之间差了什么导致Object
不是可迭代对象?
console.dir(arr)
console.dir(obj)
通过打印发现,arr相比obj多了一个Symbol.iterable
的方法,这个就是迭代的标识,有这个方法的就是可迭代对象,没有的就是不可迭代对象。
Symbol.iterable
也可以叫做迭代器,那么我们能否根据for...of
迭代数组的原理让obj也能够被迭代呢?手写一个迭代器,说干就干!
let obj = {
a: '1',
b: '2',
c: '3'
}
//首先给obj的原型添加一个Symbol.iterator方法
obj[Symbol.iterator] = function () {
//迭代器首先要有一个迭代协议,是怎么进行这个迭代进行规定,下面就是规定迭代obj的values值
let values = Object.values(obj)
//获取obj的所有values
let index = 0
//注意这里是固定写法,return返回的是一个对象,对象里面需要有一个next函数,next函数里面再返回一个对象
return {
next() {
if (index >= values.length) {
//当index大于等于values.length时,循环结束
return {
done: true
//done代表循环,true为循环结束,false为循环未结束,继续进行
}
} else {
return {
value: values[index++],
//value表示循环返回的值
done: false
}
}
}
}
}
//这时再使用for...of就能正常打印出来obj的values的值
for (let value of obj) {
console.log(value)
}
// '1','2','3'
通过上面代码我们简单实现了一个迭代器,那为什么for...of
就可以打印出来呢,其实我们还可以再细致的拆分
let values = obj[Symbol.iterator]
console.log(values.next()) //{done:false,value:1}
console.log(values.next()) //{done:false,value:2}
console.log(values.next()) //{done:false,value:3}
console.log(values.next()) //{done:true}
从上面的打印我们可以看到,每次执行values.next()
的结果都不一样,一直到循环结束,其实for...of
就是帮助我们做了这样一个事情,只不过吧里面的value
赋给for...of
里面的val
这个迭代器是我们自己手写的,迭代协议也是我们自己定义的,刚才我们拿到的是values值,下面定义一个keys的
let obj = {
a: '1',
b: '2',
c: '3'
}
//首先给obj的原型添加一个Symbol.iterator方法
obj[Symbol.iterator] = function () {
//迭代器首先要有一个迭代协议,是怎么进行这个迭代进行规定,下面就是规定迭代obj的keys值
let keys = Object.keys(obj)
//获取obj的所有keys
let index = 0
//注意这里是固定写法,return返回的是一个对象,对象里面需要有一个next函数,next函数里面再返回一个对象
return {
next() {
if (index >= keys.length) {
//当index大于等于values.length时,循环结束
return {
done: true
//done代表循环,true为循环结束,false为循环未结束,继续进行
}
} else {
return {
value: keys[index++],
//value表示循环返回的值
done: false
}
}
}
}
}
//这时再使用for...of就能正常打印出来obj的values的值
for (let value of obj) {
console.log(value)
}
// a,b,c
如果想要得到keys和values,可以直接让values返回一个对象
return {
value:{
key:keys[index],
value:obj[keys[index++]]
}
}
for (let value of obj) {
console.log(value)
}
// {key:'a',value:'1'}
// {key:'b',value:'2'}
// {key:'c',value:'3'}