for...of为什么不能遍历对象
for of 是我们经常使用的一个属性,它可以遍历数组,遍历类数组对象,如:Set 和 Map 等。但是for of并不能遍历对象,for of只能遍历可迭代数据,可迭代数据在原型上有 [Symbol.Iterator] 方法,并且在遍历数据时,会调用其 [Symbol.Iterator] 方法。
- 目前有[Symbol.iterator]方法的类型有:
- 字符串 String
- 数组 Array
- 映射 Map
- 集合 Set
- arguments对象
- Nodelist等DOM集合
Number与Object实例无 [Symbol.iterator] 方法,因此不能使用for of遍历。
如何使用for...of遍历对象
如果想要使用for of遍历对象,便需要重写其[Symbol.iterator]方法
重写Object实例的[Symbol.iterator]方法
- 重写next函数,原因:遍历的每次取值都会调用一次next函数
- 重写reterun函数,原因:遍历提前中止,会调用return函数,比如:break等
- 可以根据需要重写[Symbol.iterator],本次以遍历对象的value为例
- 补充:
- 获取对象的值,使用:Object.values(obj),本案例使用该方法
- 获取对象的键,使用:Object.keys(obj)
- 获取对象的键/值,使用:Object.entries(obj)
let obj = {name:'zhangsan',age:18,habby:'basketball'}
// 重写[Symbol.iterator]方法
obj[Symbol.iterator] = function(){
let index = 0
// 返回next函数
return {
// next函数需要返回两个参数:每次迭代返回的值value,迭代是否结束的标志done
next(){
// 取出对象的所有值,写在next方法中可以避免在迭代过程中obj发生变化
let values = Object.values(obj)
if (index < values.length){
return {
done:false, // 遍历还未结束
value:values[index++] // index++先运算,再加1
}
}else{
return {
done:true // 遍历结束
}
}
},
// 在for of中提前中止迭代,会自动调用这个return方法(比如break,continue,throw等)
return(){
console.log('Exiting early')
return {done:true}
}
}
}
测试for of遍历对象
for (let k of obj){
console.log(k) // zhangsan 18 basketball
}
/*---------*/
for (let k of obj){
if (k==18){
continue
}
console.log(k) //zhangsan basketball Exiting early
}
/*---------*/
for (let k of obj){
if (k==18){
break
}
console.log(k) //zhangsan Exiting early
}
利用生成器改写之前的代码
- 由于重写后的[Symbol.iterator]其实就是一个生成器,在生成器中本身就具有next,return,throw方法,因此可以利用生成器改写之前的代码,使代码更简洁。
注意:以下写法未解决,在调用next方法的过程中,对象发生变化的情况,大家有更好的解决方法,麻烦评论留言啦~
let obj = {name:'zhangsan',age:18,habby:'basketball'}
// 重写[Symbol.iterator]方法,function加上*号表示是生成器函数
obj[Symbol.iterator] = function*(){
let values = Object.values(obj)
// yield加上*表示挨个取出里面的元素
yield * values
}
补充拓展:
在使用以下方法时,都会调用[Symbol.iterator]方法:
- for-of 循环
- 数组解构
- 扩展操作符
- Array.from()
- 创建集合
- 创建映射
- Promise.all()接收由期约组成的可迭代对象
- Promise.race()接收由期约组成的可迭代对象
- yield*操作符,在生成器中使用
本文参考:《JavaScript高级程序设计》第七章 迭代器与生成器