简介
在前端开发中,遍历对象属性是开发中最常见的操作之一。许多开发者初学时习惯使用 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]);
});
优点:
- 只遍历自身属性(不包括原型)
- 搭配
map
、filter
、reduce
可实现函数式处理
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 | ❌ 不会 | 解构赋值配合使用更清晰,适合写复杂逻辑或中途 break 、continue | 语法稍长但更可控、不能直接 break forEach | 遍历对象键值对,逻辑复杂、需要中断等控制流程时使用 |