||

使用Object.keys和for-in遍历对象的区别

使用 Object.keys()for in 都可以遍历对象,但他们之间也有些不同,使用不当时会产生错误,并且很难排查到问题。本篇博文介绍 Object.keysfor 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 的值为 undefinednull0NaNfalse 时都为 false;
  • typeof 检测 object 类型能力有限,nullarray 都会被视为 object

类似文章