注:下文提到的对象指的是由字面量形式
{}
或new Object()
形式创建的对象。
我们的需求是:遍历出一个对象的【所有自身属性】,注意两个关键点:
- 所有(包括可枚举和不可枚举的属性)
- 自身(只包含自身属性,不考虑继承属性)
下面的代码创建了一个 obj
对象,它包含 6
个自身属性和 2
个继承属性。
js
/**
* 通过 Object.create() 创建 obj
* 使其具有 x 和 y 两个继承属性
*/
const obj = Object.create( {
x: 100,
y: 200
} );
/**
* 在 obj 上添加四个属性
*/
obj.a = 10;
obj.b = 20;
obj[ Symbol( "1" ) ] = 30;
obj[ Symbol( "2" ) ] = 40;
/**
* 使用 Object.defineProperties() 再添加两个属性
*/
Object.defineProperties( obj, {
c: {
enumerable: true,
value: 50
},
d: {
enumerable: false,
value: 60
}
} );
理想情况下,满足需求的预期结果将返回 6
个属性,如下所示:
js
[ 'a', 'b', 'c', 'd', Symbol(1), Symbol(2) ]
接下来我们使用四种不同的方案来遍历 obj
对象,看看哪一个符合预期。
方案一:for...in
这是最常用的一种遍历对象的方式:
js
const result = [];
for ( const key in obj ) {
result.push( key );
}
console.log( result );
结果如下:
css
[ 'a', 'b', 'c', 'x', 'y' ]
可以看到,并没有返回预期的结果。这里就需要弄清楚 for...in
的遍历规则 ,它只会遍历 "可枚举属性",而 Symbol
类型是不可枚举的,enumerable: false
的属性也是不可枚举的,因此均无法通过 for...in
遍历出来,并且除自身属性外,它还会遍历继承的属性 。所以,for...in
无法满足我们的需求。
方案二:Object.keys()
现在我们来试一下 Object.keys()
方式:
js
console.log( Object.keys( obj ) );
结果如下:
js
[ 'a', 'b', 'c' ]
由结果可见,Object.keys()
只会遍历自身的属性,不会遍历继承属性,这一点符合我们的需求,但是它依旧无法遍历出那些不可枚举的属性。
方案三:Object.getOwnPropertyNames()
我们再试试这个方法:
js
console.log( Object.getOwnPropertyNames( obj ) );
结果如下:
js
[ 'a', 'b', 'c', 'd' ]
这里我们发现,Object.getOwnPropertyNames()
同样也只会遍历自身的属性,不会遍历继承属性,并且它还能遍历出 enumerable: false
的不可枚举属性,但对于 Symbol
属性它却无能为力。这距离我们的需求就只差一个 Symbol
属性的问题了,我们可以借助另一个方法辅助它来实现我们的需求,这个方法是 Object.getOwnPropertySymbols()
,它会返回对象自身的所有 Symbol
属性,两者结合起来就满足了我们的需求,代码如下:
js
const props = Object.getOwnPropertyNames( obj );
const symbols = Object.getOwnPropertySymbols( obj );
console.log( props.concat( symbols ) );
结果如下:
js
[ 'a', 'b', 'c', 'd', Symbol(1), Symbol(2) ]
这种方案借助了两个方法实现了最终需求,不过略显繁琐,下面的方案四则提供了终极方案。
方案四:Reflect.ownKeys()
这个方法用最简单的方式实现了我们的需求,代码如下:
js
console.log( Reflect.ownKeys( obj ) );
结果如下:
js
[ 'a', 'b', 'c', 'd', Symbol(1), Symbol(2) ]
此方法会返回一个数组,包含了指定对象的所有自身属性,包括可枚举和 enumerable: false
的不可枚举属性,以及 Symbol
属性。