使用Object.keys和for-in遍历对象的区别
使用 Object.keys() 和 for in 都可以遍历对象,但他们之间也有些不同,使用不当时会产生错误,并且很难排查到问题。本篇博文介绍 Object.keys 和 for in 遍历对象的异同点,以及使用注意事项。
Object.keys
Object.keys() 返回所有属于当前对象的可枚举字段名组成的数组,这里的几个关键词:
- 当前对象,而非对象的原型链;
- 可枚举属性,即属性描述符的 enumerable 为 false 时无法被检测到;
- 返回数组;
使用代码举例:
const person = {
name: 'jack',
age: 20
};
console.log(Object.keys(person)); // ['name', 'age']
当对象 person 的 name 属性为不可枚举时:
const descriptor = Object.getOwnPropertyDescriptor(person, 'name');
descriptor.enumerable = false;
Object.defineProperty(person, 'name', descriptor);
console.log(Object.keys(person)); // ['age']
不可枚举的属性不会被检测出,所以只打印了 age 一个字段名。
给 person 的 __proto__ 指定一个对象,再进行检测:
const person = {
name: 'jack',
age: 20
};
person.__proto__.word = 'hello';
console.log(Object.keys(person)); // ['name', 'age']
可以看到 Object.keys 无法检测原型链上的属性。
for in
for in 用于遍历给定对象原型链上的所有可枚举字段,几个关键词:
- 对象的原型链,而非对象本身;
- 可枚举属性,即属性描述符的 enumerable 为 false 时无法被检测到;
相比 Object.keys ,使用 for in 迭代对象时可以访问到对象原型链上的字段,例如:
const person = {
name: 'jack',
age: 20
};
person.__proto__.word = 'hello';
for (let key in person) { console.log(key) }
// name
// age
// word
同样,对于不可枚举的属性,使用 for in 也是迭代不到的,这里代码不再赘述。
总结
由上面的代码可知,for in 有能力检测原型链上的属性,相比于 Object.keys 似乎更强大,但这并不是好事。
如果对象实例之前存在继承关系,使用 fon in 迭代对象时的副作用将更明显,本意上可能是想遍历当前实例的属性,结果访问到原型链上属性,这有可能会带来意想不到的问题。
所以,在遍历对象时,首先弄明白到底要遍历什么,然后使用合理的方法进行遍历。
字段检查
很多时候,在一个接受可变参数的函数的函数中,开发者有时会通过检查某个字段来确定当前的对象,需要注意:
- 语义上需要明确,检测的是当前对象的字段,还是当前对象原型链上的字段;
- if (obj.xxx) 在 xxx 不存在,或 xxx 的值为 undefined 、null 、0 、NaN 和 false 时都为 false;
- typeof 检测 object 类型能力有限,null 和 array 都会被视为 object;