别再用 for...in 遍历对象了,有坑还不专业!

284 阅读4分钟

简介

在前端开发中,遍历对象属性是开发中最常见的操作之一。许多开发者初学时习惯使用 for...in,但它其实存在不少隐性问题,一不小心就可能踩坑。随着 ES6 的普及,我们已经有了更现代、更安全的替代方案,比如 Object.keys()Object.values()Object.entries() 等。

for...in 的用法与隐藏问题

for...in作为初学者的最爱,因为它的语法非常简单,很容易上手

语法

for (const key in object) {
  // 循环体
}

使用示例

const obj = { a: 1, b: 2 };

for (const key in obj) {
  console.log(key, obj[key]);
}
// 输出:a 1, b 2

for...in 会遍历对象的所有可枚举属性,包括原型链上的属性,这会导致使用它遍历对象会带来一定风险。

  • 遍历了原型链上的属性
const obj = { a: 1 };
Object.prototype.b = 2;

for (const key in obj) {
  console.log(key); // 输出 a 和 b
}

如果你没有用 hasOwnProperty() 过滤,很容易误操作原型链上的属性。

hasOwnProperty()Object.prototype 上的一个方法,用于判断某个属性是否是对象自身“直接拥有的属性”,而不是继承自原型链的属性。

  • 属性顺序不可预测

for...in 不保证遍历顺序与属性定义顺序一致,这在处理顺序敏感的数据时可能带来问题。

  • 性能不佳,写法不直观

与现代 API 相比,for...in 更容易出错,语义不够清晰。

现代化的对象遍历方式

Object.keys()

Object.keys() 返回对象自身的可枚举属性名数组, 搭配 forEach() 可进行遍历,用于获取对象自身的可枚举属性名

const obj = { a: 1, b: 2 };

const keyList = Object.keys(obj)

console.log(keyList) // [a,b]

Object.keys(obj).forEach(key => {
  console.log(key, obj[key]); 
});

优点

  • 只遍历自身属性(不包括原型)
  • 搭配 mapfilterreduce 可实现函数式处理

Object.values()

用于获取对象自身的值数组,适合只关注值的场景。

const obj = { a: 1, b: 2 };

const valueList = Object.values(obj)

console.log(valueList) // [1,2]

Object.values(obj).forEach(value => {
  console.log(value);
});

Object.entries

Object.entries() 方法返回一个数组,其中每个元素都是一个 [key, value] 的二元组(键值对)。这种方法非常适合用来遍历对象的键值对,特别是结合 forEach()for...of 或其他高阶函数使用时,可以让代码更加简洁且语义清晰。

基本语法

Object.entries(object);
  • 返回一个二维数组,其中每个元素是一个数组 [key, value]
  • 该方法只遍历对象的自身可枚举属性,不包括继承的属性。

Object.entries() + forEach()

forEach() 是数组方法,用来对每个元素执行指定的回调函数。可以将 Object.entries() 结合 forEach() 来遍历对象的所有键值对。

const obj = { name: '快乐就是哈哈哈', age: 18 };

Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});
// 输出:
// name: 快乐就是哈哈哈
// age: 18

  • 优点:简洁、可链式调用
  • 适用场景:适合直接执行操作,比如打印、修改对象属性等。

Object.entries() + for...of

for...of 是一种用于遍历可迭代对象(如数组、Map、Set)的语法,结合 Object.entries(),我们可以更优雅地处理对象的键值对。

const person = { name: '快乐就是哈哈哈', age: 18 };

for (const [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

  • 优点:使用结构赋值,代码更加简洁,易于阅读
  • 适用场景:遍历对象的键值对,执行相关操作。

总结

遍历方式是否遍历原型链属性优点缺点适用场景
for...in✅ 会语法简单,初学者容易上手会遍历原型链属性、属性顺序不可控、需手动过滤、性能较差快速遍历对象所有属性(包含继承),需搭配 hasOwnProperty()过滤
Object.keys()+ forEach❌ 不会只遍历自身属性、函数式写法灵活、可组合 map/filter/reduce只能拿键,值需通过索引访问,略繁琐遍历键名并操作对应值
Object.values()+ forEach❌ 不会语法简洁,适用于只关心值的场景无法获取键,不能访问完整的键值对只需访问对象值的场景
Object.entries()+ forEach❌ 不会结构清晰,语义明确,键值都可获取相比 keys稍重,但不明显遍历对象键值对并执行相关操作
Object.entries()+ for...of❌ 不会解构赋值配合使用更清晰,适合写复杂逻辑或中途 breakcontinue语法稍长但更可控、不能直接 break forEach遍历对象键值对,逻辑复杂、需要中断等控制流程时使用