JavaScript中有多种方式遍历对象中的属性,本文中所说的对象都是普通的Object对象,不包括Array、Map、Set等实现了Iterator接口的对象。
对象属性的遍历主要受几方面的影响: 能否遍历原型链上的属性、能否遍历不可枚举属性、能否遍历Symbol属性。
遍历对象属性的方式包括: for...in
、Object.keys()
、Reflect.ownKeys()
、Object.getOwnPropertyNames()
、Object.getOwnPropertySymbols()
。
为了测试以上几种方式的区别,我们首先构造一组包含自身属性及原型链属性、可枚举属性及不可枚举属性、普通非Symbol属性及Symbol属性。
const c = Symbol('c');
const father = {
a: 1,
b: 2,
[c]: 3,
getA() {
return this.a;
},
getB() {
return this.b;
}
};
Object.defineProperty(father, 'd', {
value: 4,
enumerable: false
});
function Child() {
this.e = 5;
this.f = 6;
const g = Symbol('g');
this[g] = 7;
};
Child.prototype = father;
const obj = new Child();
Object.defineProperty(obj, 'h', {
value: 8,
enumerable: false
});
const i = Symbol('i');
Object.defineProperty(obj, i, {
value: 9,
enumerable: false
});
以上属性按照不同维度可以划分如下
下面使用不同的方式对上述对象进行属性遍历。
for (var key in obj) {
console.log(key);
}
// 输出:
// e
// f
// a
// b
// getA
// getB
以上示例说明for-in具有以下性质:
结论: for...in
遍历自身及原型链上的可枚举的非Symbol属性,其遍历原型链上的属性,其他方法都不遍历原型链上的属性。
console.log(Object.keys(obj));
// 输出:
// ['e', 'f']
以上示例说明Object.keys()具有以下性质:
结论: Object.keys()
遍历自身的可枚举的非Symbol属性,所以Object.keys()是遍历最严格的,方法名中的keys表示同时遍历普通属性以及Symbol。
// console.log(Reflect.ownKeys(obj));
// 输出:
// ['e', 'f', 'h', Symbol(g), Symbol(i)]
以上示例说明Reflect.ownKeys()具有以下性质:
结论: Reflect.ownKeys()
遍历自身的所有属性,不考虑是否可枚举以及是否是Symbol,方法名ownKeys中的own表示遍历自身属性,Keys表示可以同时遍历普通属性以及Symbol。
console.log(Object.getOwnPropertyNames(obj));
// 输出:
// ['e', 'f', 'h']
以上示例说明Object.getOwnPropertyNames()具有以下性质:
结论: Object.getOwnPropertyNames()
遍历自身的非Symbol属性,不考虑是否可枚举,getOwnPropertyNames中的Own表示遍历自身属性,PropertyNames表示只遍历普通属性,不遍历Symbol。
// console.log(Object.getOwnPropertySymbols(obj));
// 输出:
// [Symbol(g), Symbol(i)]
以上示例说明Object.getOwnPropertySymbols()具有以下性质:
结论: Object.getOwnPropertySymbols()
只遍历自身的Symbol,更强调own和symbol,不考虑是否可枚举,方法名getOwnPropertySymbols中的Own表示只遍历自身属性,PropertySymbols表示只遍历Symbol。
从方法的方法名就能看出该方法能遍历哪些属性
遍历方式 | 是否遍历原型链属性 | 是否遍历非枚举类型属性 | 是否遍历Symbol属性 |
---|---|---|---|
for…in | 遍历原型链属性 | 不遍历非枚举类型属性 | 不遍历Symbol属性 |
Object.keys() | 不遍历原型链属性 | 不遍历非枚举类型属性 | 不遍历Symbol属性 |
Reflect.ownKeys() | 不遍历原型链属性 | 可遍历非枚举类型属性 | 可遍历Symbol属性 |
Object.getOwnPropertyNames() | 不遍历原型链属性 | 可遍历非枚举类型属性 | 不遍历Symbol属性 |
Object.getOwnPropertySymbols() | 不遍历原型链属性 | 可遍历非枚举类型属性 | 只遍历Symbol属性 |
for...in
遍历自身及原型链上的可枚举的非Symbol属性,其遍历原型链上的属性,其他方法都不遍历原型链上的属性。Object.keys()
遍历自身的可枚举的非Symbol属性,所以Object.keys()是遍历最严格的,方法名中的keys表示同时遍历普通属性以及Symbol。Reflect.ownKeys()
遍历自身的所有属性,不考虑是否可枚举以及是否是Symbol,方法名ownKeys中的own表示遍历自身属性,Keys表示可以同时遍历普通属性以及Symbol。它的返回值等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
。Object.getOwnPropertyNames()
遍历自身的非Symbol属性,不考虑是否可枚举,getOwnPropertyNames中的Own表示遍历自身属性,PropertyNames表示只遍历普通属性,不遍历Symbol。Object.getOwnPropertySymbols()
只遍历自身的Symbol,不考虑是否可枚举,更强调own和symbol,方法名getOwnPropertySymbols中的Own表示只遍历自身属性,PropertySymbols表示只遍历Symbol。参考:
for…in
Object.keys()
Reflect.ownKeys()
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()