属性遍历方法

JavaScript 中遍历对象属性的 5 种方法及其区别

问题

JavaScript 中有哪些遍历对象属性的方法?它们有什么区别?

解答

ES6 提供了 5 种遍历对象属性的方法:

1. for…in

遍历对象自身和继承的可枚举属性(不含 Symbol)。

const parent = { inherited: 'parent' };
const obj = Object.create(parent);
obj.name = 'test';
obj[Symbol('id')] = 123;

for (let key in obj) {
  console.log(key);
}
// 输出: name, inherited(包含继承属性)

2. Object.keys()

返回对象自身可枚举属性的键名数组(不含 Symbol 和继承属性)。

const obj = { a: 1, b: 2 };
Object.defineProperty(obj, 'c', {
  value: 3,
  enumerable: false // 不可枚举
});
obj[Symbol('d')] = 4;

console.log(Object.keys(obj));
// 输出: ['a', 'b']

3. Object.getOwnPropertyNames()

返回对象自身所有属性的键名数组(包含不可枚举,不含 Symbol)。

const obj = { a: 1 };
Object.defineProperty(obj, 'b', {
  value: 2,
  enumerable: false
});
obj[Symbol('c')] = 3;

console.log(Object.getOwnPropertyNames(obj));
// 输出: ['a', 'b'](包含不可枚举的 b)

4. Object.getOwnPropertySymbols()

返回对象自身所有 Symbol 属性的键名数组。

const sym1 = Symbol('x');
const sym2 = Symbol('y');
const obj = {
  a: 1,
  [sym1]: 2,
  [sym2]: 3
};

console.log(Object.getOwnPropertySymbols(obj));
// 输出: [Symbol(x), Symbol(y)]

5. Reflect.ownKeys()

返回对象自身所有属性的键名数组(包含 Symbol 和不可枚举)。

const sym = Symbol('id');
const obj = {
  [sym]: 1,
  b: 2,
  a: 3
};
Object.defineProperty(obj, 'c', {
  value: 4,
  enumerable: false
});

console.log(Reflect.ownKeys(obj));
// 输出: ['b', 'a', 'c', Symbol(id)]

遍历顺序

以上 5 种方法遵循相同的遍历顺序:

const obj = {
  2: 'two',
  b: 'b',
  1: 'one',
  a: 'a',
  [Symbol('s')]: 'symbol'
};

console.log(Reflect.ownKeys(obj));
// 输出: ['1', '2', 'b', 'a', Symbol(s)]
// 顺序:数值键升序 -> 字符串键按添加顺序 -> Symbol 键按添加顺序

方法对比表

方法自身属性继承属性不可枚举Symbol
for…in
Object.keys()
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Reflect.ownKeys()

关键点

  • for...in 是唯一会遍历继承属性的方法,常配合 hasOwnProperty 过滤
  • Object.keys() 最常用,只返回自身可枚举的字符串键
  • Reflect.ownKeys() 最全面,等于 Object.getOwnPropertyNames() + Object.getOwnPropertySymbols()
  • 遍历顺序固定:数值键升序 → 字符串键插入序 → Symbol 键插入序
  • Object.values()Object.entries()Object.keys() 规则相同