在 JavaScript 中,我们经常需要遍历对象的属性,但不同的遍历方法可能会导致属性枚举顺序的不同。例如,在构建表单序列化、配置对象处理、或者在进行对象属性的深度复制时,属性的顺序可能会影响最终结果。
JavaScript 中的属性枚举方法
JavaScript 提供多种方法枚举对象的属性,它们的枚举顺序和行为有所不同:
1、for…in 循环: 遍历对象自身及其原型链上所有可枚举的属性。
- 顺序不确定,取决于 JavaScript 引擎。
- 通常用于遍历所有可枚举属性,而不太关心顺序。
2、Object.keys(): 返回一个数组,包含对象自身所有可枚举的字符串类型的键名。
- 顺序不确定,取决于 JavaScript 引擎。
- 适用于只需要获取对象自身可枚举属性的场景。
3、Object.getOwnPropertyNames(): 返回一个数组,包含对象自身所有的属性键名,包括不可枚举的属性,但不包括 Symbol 类型的键名。
- 顺序确定:
- 首先按照升序枚举数值键。
- 然后按照插入顺序枚举字符串键。
- 适用于需要获取对象自身所有属性,包括不可枚举属性的场景。
4、Object.getOwnPropertySymbols(): 返回一个数组,包含对象自身所有的 Symbol 类型的属性键名。
- 顺序确定,按照 Symbol 属性插入对象的顺序排列。
- 适用于需要获取对象自身所有 Symbol 类型属性的场景。
5、Object.assign(): 用于将源对象的所有可枚举属性复制到目标对象。
- 属性枚举顺序与
Object.getOwnPropertyNames()
和Object.getOwnPropertySymbols()
相同, 首先枚举数值键,然后枚举字符串和符号键,并按照插入顺序排列。 - 适用于需要复制对象属性并保持特定顺序的场景。
示例代码
以下代码演示了不同属性枚举方法的顺序:
let k1 = Symbol('k1'), k2 = Symbol('k2');
let o = {
1: 1,
first: 'first',
[k1]: 'sym1',
second: 'second',
0: 0
};
o[k2] = 'sym2';
o[3] = 3;
o.third = 'third';
o[2] = 2;
// for...in 循环 - 顺序不确定
for (let key in o) {
console.log(key, o[key]);
}
// Object.keys() - 顺序不确定
console.log(Object.keys(o));
// Object.getOwnPropertyNames() - 顺序确定
console.log(Object.getOwnPropertyNames(o)); // ["0", "1", "2", "3", "first", "second", "third"]
// Object.getOwnPropertySymbols() - 顺序确定
console.log(Object.getOwnPropertySymbols(o)); // [Symbol(k1), Symbol(k2)]
// Object.assign() - 顺序确定,与 Object.getOwnPropertyNames() 和 Object.getOwnPropertySymbols() 相同
let newObj = Object.assign({}, o);
console.log(Object.getOwnPropertyNames(newObj)); // ["0", "1", "2", "3", "first", "second", "third"]
console.log(Object.getOwnPropertySymbols(newObj)); // [Symbol(k1), Symbol(k2)]