大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript
等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter
等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了常见的前端开发技术,我还擅长3D开发,熟练使用Three.js
进行3D图形绘制,并在虚拟现实与数字孪生技术上积累了丰富的经验,特别是在虚幻引擎开发方面,有着深入的理解和实践。

我一直认为技术的不断探索和实践是进步的源泉,近年来,我深入研究大数据算法的应用与发展,尤其在数据可视化和交互体验方面,取得了显著的成果。我也注重与团队的合作,能够有效地推动项目的进展和优化开发流程。现在,我担任全栈工程师,拥有CSDN博客专家认证及阿里云专家博主称号,希望通过分享我的技术心得与经验,帮助更多人提升自己的技术水平,成为更优秀的开发者。
技术qq交流群:906392632
大家好,我是小杨,一个做了6年前端的老司机。今天咱们来聊聊JavaScript中遍历对象属性的两种常见方式------for...in循环和Object.keys()方法。虽然它们都能用来遍历对象属性,但在实际使用中却有不少区别,选错了可能会导致一些意想不到的bug。
为什么这个问题值得讨论?
上周我在review团队代码时,发现一个有趣的bug:一个同事用for...in遍历对象属性时,意外获取到了原型链上的方法,导致数据处理出错。这让我意识到,很多开发者对这两种遍历方式的区别理解不够深入。今天我就带大家彻底搞懂它们的差异,避免踩坑。
基本用法对比
for...in 循环
javascript
const myCar = {
make: 'Toyota',
model: 'Camry',
year: 2020
};
for (let key in myCar) {
console.log(`${key}: ${myCar[key]}`);
}
// 输出:
// make: Toyota
// model: Camry
// year: 2020
Object.keys()
javascript
const myCar = {
make: 'Toyota',
model: 'Camry',
year: 2020
};
Object.keys(myCar).forEach(key => {
console.log(`${key}: ${myCar[key]}`);
});
// 输出:
// make: Toyota
// model: Camry
// year: 2020
看起来它们都能正常工作,那么区别在哪里呢?
核心区别详解
1. 原型链属性的处理
这是最大的区别!for...in会遍历对象自身的属性+原型链上的可枚举属性,而Object.keys()只返回对象自身的可枚举属性。
来看个例子:
javascript
function Car() {
this.make = 'Toyota';
}
Car.prototype.model = 'Camry';
const myCar = new Car();
myCar.year = 2020;
// 使用for...in
console.log('for...in结果:');
for (let key in myCar) {
console.log(key); // 输出make, year, model
}
// 使用Object.keys
console.log('Object.keys结果:');
console.log(Object.keys(myCar)); // 输出["make", "year"]
在实际项目中,这种差异可能导致严重问题。比如我在去年做的一个表单处理工具中,就因为这个特性导致表单意外提交了原型链上的方法。
2. 性能差异
虽然现代JavaScript引擎已经优化得很好,但在大规模数据下,Object.keys()通常比for...in稍快,因为它不需要检查原型链。
我做了一个简单测试:
javascript
const largeObj = {};
for (let i = 0; i < 1000000; i++) {
largeObj[`key${i}`] = i;
}
console.time('for...in');
for (let key in largeObj) {}
console.timeEnd('for...in');
console.time('Object.keys');
Object.keys(largeObj).forEach(key => {});
console.timeEnd('Object.keys');
在我的测试中,Object.keys()版本通常快10-15%。
3. 返回值的不同
- for...in是一个循环结构,直接遍历属性
- Object.keys()返回一个包含所有属性名的数组,这意味着你可以使用数组的所有方法
javascript
const myCar = {
make: 'Toyota',
model: 'Camry',
year: 2020
};
// 使用数组方法过滤属性
Object.keys(myCar)
.filter(key => key !== 'year')
.forEach(key => {
console.log(key); // 输出make, model
});
4. 可枚举属性的处理
两者都只遍历可枚举属性,但for...in的行为可能会因为原型链上的不可枚举属性而变得不可预测。
javascript
const obj = Object.create(null, {
a: { value: 1, enumerable: true },
b: { value: 2, enumerable: false }
});
console.log('for...in:');
for (let key in obj) {
console.log(key); // 只输出a
}
console.log('Object.keys:');
console.log(Object.keys(obj)); // 只输出["a"]
实际项目中的选择建议
根据我的经验,以下是一些使用场景建议:
使用for...in的情况:
- 需要遍历对象及其原型链上的所有可枚举属性
- 在你知道对象结构明确且没有原型链干扰的情况下
- 在需要中断循环时(可以使用break)
使用Object.keys()的情况:
- 只需要对象自身的属性
- 需要使用数组方法处理属性名时
- 需要将属性名转换为数组时
- 在性能敏感的场景下
常见陷阱与解决方案
陷阱1:意外遍历原型链属性
解决方案:
javascript
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 确保是对象自身的属性
}
}
或者直接使用Object.keys()
陷阱2:修改对象属性导致意外行为
javascript
const obj = { a: 1, b: 2, c: 3 };
Object.keys(obj).forEach(key => {
console.log(key);
delete obj.b; // 可能导致意外行为
});
解决方案:避免在遍历过程中修改对象结构
陷阱3:Symbol属性不会被遍历
两者都不会遍历Symbol属性,如果需要遍历Symbol属性,可以使用Object.getOwnPropertySymbols()
现代JavaScript的替代方案
在ES2017之后,我们还有更多选择:
Object.values()
javascript
const values = Object.values(myCar);
// ["Toyota", "Camry", 2020]
Object.entries()
javascript
for (const [key, value] of Object.entries(myCar)) {
console.log(key, value);
}
这些方法同样只遍历对象自身的可枚举属性,但提供了更便捷的访问方式。
总结
- for...in:遍历对象自身+原型链的可枚举属性,适合需要原型链属性的场景
- Object.keys() :只返回对象自身的可枚举属性,更安全、更常用
- 现代项目推荐优先使用Object.keys()或Object.entries()
- 在需要原型链属性时使用for...in,但要配合hasOwnProperty检查
记住,选择哪种方式取决于你的具体需求。在团队项目中,保持一致性也很重要。希望这篇文章能帮你理清这两种遍历方式的区别,避免在实际开发中踩坑!
如果你有其他关于对象遍历的经验或技巧,欢迎在评论区分享交流!