如何使用for...of遍历对象

985 阅读2分钟

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]方法:

  1. for-of 循环
  2. 数组解构
  3. 扩展操作符
  4. Array.from()
  5. 创建集合
  6. 创建映射
  7. Promise.all()接收由期约组成的可迭代对象
  8. Promise.race()接收由期约组成的可迭代对象
  9. yield*操作符,在生成器中使用

本文参考:《JavaScript高级程序设计》第七章 迭代器与生成器