for...in、Object.keys() 和 Reflect.ownKeys() 三种遍历对象属性的方法异同

317 阅读3分钟

在 JavaScript 中,for...inObject.keys()Reflect.ownKeys() 是三种常用来遍历对象属性的方法,但它们的行为和适用场景各不相同。让我们详细探讨一下它们的工作原理、适用场景以及各自的区别。

1. for...in 循环

作用:
  • for...in 用于遍历对象的可枚举属性,不仅遍历对象自身的属性,也会遍历其原型链上的可枚举属性。
特点:
  • 遍历对象自身和继承的可枚举属性。
  • 只会遍历可枚举的属性(enumerable: true)。
  • 返回的是属性名称(键),而非属性值。
适用场景:
  • 适合遍历所有可枚举属性的场景,特别是当你需要遍历原型链上的属性时。
示例:
const obj = { a: 1, b: 2 };
Object.prototype.c = 3;

for (let key in obj) {
  console.log(key); // 输出: "a", "b", "c"(包括继承的 c)
}
缺点:
  • 如果对象继承了原型链上的属性,且这些属性是可枚举的,for...in 也会遍历这些属性,可能导致意外行为。
  • 因为它遍历的是属性名称,若只想遍历对象自身的属性,需要配合 hasOwnProperty() 来过滤掉继承的属性:
for (let key in obj) {
  if (obj.hasOwnProperty(key)) {
    console.log(key); // 仅输出 "a", "b"
  }
}

2. Object.keys()

作用:
  • Object.keys() 返回对象自身的可枚举属性组成的数组,不包括原型链上的属性。
特点:
  • 仅返回对象自身的可枚举属性。
  • 返回结果是一个数组,数组中的每一项是对象的属性名称。
适用场景:
  • 适合需要获取对象自身的属性(不包括继承的属性),并且希望结果以数组形式呈现的场景。
示例:
const obj = { a: 1, b: 2 };
Object.prototype.c = 3;

console.log(Object.keys(obj)); // 输出: ["a", "b"]
优点:
  • 不会遍历原型链上的属性,因此更加安全和常用,尤其在处理仅限对象自身属性的场景时。

3. Reflect.ownKeys()

作用:
  • Reflect.ownKeys() 返回对象的所有自身属性,包括可枚举和不可枚举的属性,且会返回符号(Symbol)属性
特点:
  • 返回对象自身的所有属性,无论它们是否可枚举。
  • 能够遍历符号属性(Symbol)。
适用场景:
  • 当你需要遍历对象的所有自身属性,包括不可枚举和 Symbol 属性时,Reflect.ownKeys() 是最全面的方法。
示例:
const obj = { a: 1, b: 2 };
Object.defineProperty(obj, 'c', { value: 3, enumerable: false });
const sym = Symbol('d');
obj[sym] = 4;

console.log(Reflect.ownKeys(obj)); // 输出: ["a", "b", "c", Symbol(d)]
优点:
  • 完整遍历对象自身的属性,包含不可枚举和 Symbol 属性,适用于高级场景。
缺点:
  • 由于它返回所有属性,包括不可枚举的属性,可能会产生不必要的结果,需要仔细处理。

三者的对比总结

方法是否遍历继承的属性是否遍历不可枚举属性是否包含 Symbol 属性返回类型
for...in属性名称(字符串)
Object.keys()属性名称数组(字符串)
Reflect.ownKeys()属性名称数组(字符串和 Symbol)
选择建议:
  • for...in:适合需要遍历所有可枚举属性(包括继承的属性)的场景。
  • Object.keys():适合只需要遍历对象自身可枚举属性的场景,使用频率较高。
  • Reflect.ownKeys():适合需要完整获取对象自身属性(包括不可枚举和符号属性)的高级用法。

通过了解这些方法的区别,可以在不同的场景中选择合适的方式来遍历对象的属性。