JavaScript 判断属性或者遍历属性: in, for in, Object.keys, Reflect.ownKeys etc.

501 阅读3分钟

判断属性存在或者遍历属性是日常开发中经常会用到。只是针对这些操作符、语句、方法,有些混淆。所以总结一下。

对象属性

对象属性目前只能是字符串值或者 symbol 值

A property key value is either an ECMAScript String value or a Symbol value

ECMAScript® 2020 Language Specification

明确了这个规则后,开始下面的说明。

Property Attributes

翻译为属性的属性?这个概念我也有点说不清,看看下面的参考

attribute和property在英语里有什么区别? - 知乎

Property Attributes 有 Value、Writable、Enumerable、Configurable。这个可以通过 Object.getOwnPropertyDescriptor 获取值。通过 Object.defineProperty 进行定义或者修改。

另外还有 Get、Set

本文的重点是讨论 Enumerable,这个值设置为 false 后,属性是不可遍历的。

但是不可遍历了,我还是需要获取这个属性该怎么办?

in 操作符

指定的属性在指定的对象或其原型链中,则in 运算符返回true

Reflect.hasin 的效果相同

这里的重点是 in 会检查原型链

console.log(length in []);  // true
// 继承的 Symbol
console.log(Symbol.iterator in []);  // true
// 继承的方法
console.log('toString' in []);  // true

for...in 语句

for...in 循环只遍历可枚举属性(包括它的原型链上的可枚举属性)

因此 for...in 的行为受到 Enumerable 的控制

for in 区别 // 遍历除了 symbol 以外的可枚举属性

let obj = {
  [Symbol('my_key')]: 1,
  enum: 2,
  nonEnum: 3
};
Object.defineProperty(obj,
  'nonEnum', { enumerable: false });
console.log(Object.keys(obj))

let obj2 = Object.create(obj)
obj2.myVal = 5
console.log(obj2);  // { myVal: 5 }

for (var prop in obj2) {
  console.log('obj2 prop: ', prop);  // myVal, enum
}

Object 对象的一些方法

Object.keys

会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致 。

// 省略上面的代码
Object.keys(obj)  // ['enum']
Object.keys(obj2)  // ['myVal']

Object.getOwnPropertyNames

返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。

// 省略上面的代码
Object.getOwnPropertyNames(obj)  // ['enum', 'nonEnum']

Object.prototype.hasOwnProperty 用来判断对象自身属性。

一个常用的判断:遍历一个对象的所有自身属性

var buz = {
  fog: 'stack'
};

for (var name in buz) {
  if (buz.hasOwnProperty(name)) {
    console.log('this is fog (' + 
      name + ') for sure. Value: ' + buz[name]);
  }
  else {
    console.log(name); // toString or something else
  }
}

Object.getOwnPropertySymbols

返回一个给定对象自身的所有 Symbol 属性的数组

// 省略上面的代码
Object.getOwnPropertySymbols(obj)  // [ Symbol(my_key) ]

Reflect.ownKeys

上面的方法操作符,饶了一圈。和 Enumerable Symbol 的有无打交道。

有没有方法能够拿到所有的属性 (我全都要.webp)

答案就是: Reflect.ownKeys

// 省略上面的代码
Reflect.ownKeys(obj)  // [ Symbol(my_key), 'enum', 'nonEnum' ]

总结

对象属性目前只能是字符串值或者 symbol 值。所以下面的情况会出现 toPrimitive 或者 toString 的转换

let obj3 = {
    [obj]: 33
}
// { '[object Object]': 33 }

当然需要使用对象作为 key, 需要用到 Map 了,这些是后话。

一般情况下都是用 Object.keys 来遍历自身可枚举属性。Object.keys 是 ES5 的标准。

以前 ES3 的时候是需要 in 操作符 和 hasOwnProperty 配合使用

全都要的情况: Reflect.ownKeys

要考虑 Enumerable 为 false 时, 用 Object.getOwnPropertyNames 要考虑 Symbol 时, 用 Object.getOwnPropertySymbols

参考链接

Symbols in ECMAScript 6

MDN

in - JavaScript | MDN Keys in Javascript objects can only be strings? - Stack Overflow

Object.prototype.hasOwnProperty() - JavaScript | MDN