獲取對象的屬性值

寫在前面

ECMAScript中,經過key獲取Objectvalue是再日常不過的操做。但是,因爲原型鏈(prototype chain)、類繼承(class extend)的存在,在你遍歷對象屬性的時候,你想要/不想要獲取原型鏈上,或者父類的屬性,結果都會不如你的預期。還有一種狀況,你要取到Symbol的屬性,卻沒法取得。javascript

本片文章將會對比幾種遍歷對象屬性的方法,讓你能夠準確的獲取到對象上的值。vue

方法對比

咱們先使用構造函數建立一個對象,在構造函數中設置其實例屬性,而後設置構造函數原型對象的屬性,而後再給實例添加屬性。java

// 構造函數
function MakeObj () {
  Object.defineProperties(this, {
    x1: {
      value: 1,
      enumerable: true
    },
    x2: {
      value: 11,
      enumerable: false
    }
  })
}
// 原型對象
Object.defineProperties(MakeObj.prototype, {
  y1: {
    value: 2,
    enumerable: true
  },
  y2: {
    value: 22,
    enumerable: false
  }
})

// 建立實例
var obj = new MakeObj()

// 設置實例對象屬性
Object.defineProperties(obj, {
  z1: {
    value: 3,
    enumerable: true
  },
  z2: {
    value: 33,
    enumerable: false
  }
})
複製代碼

咱們對比一下Object.keysObject.getOwnPropertyNamesfor...infor...of框架

Object.keys(obj) // ["x1", "z1"]
Object.getOwnPropertyNames(obj) // ["x1", "x2", "z1", "z2"]

var forIn = []
for (let x in obj) {
  forIn.push(x)
}
forIn // ["x1", "z1", "y1"]

var forOf = []
for (let x in obj) {
  forOf.push(x)
}
forOf // ["x1", "z1", "y1"]
複製代碼

用表格看更清晰:函數

方法 實例屬性 原型對象屬性
可枚舉(x1,z1) 不可枚舉(x2,z2) 可枚舉(y1) 不可枚舉(y2)
Object.keys × × ×
Object.getOwnPropertyNames × ×
for...in × ×
for...of × ×


因此,若是你想要:ui

  • 僅遍歷可枚舉的實例屬性,使用Object.keys
  • 遍歷包括枚舉和不可枚舉的實例屬性,使用Object.getOwnPropertyNames
  • 若是你要遍歷包括可枚舉的實例屬性和原型對象上的可枚舉屬性,使用for...infor...of

獲取Symbol屬性值

若是咱們設置了Symbol屬性呢?this

var sml = Symbol('key')
var obj = {
 x: 1,
 [sml]: 'symbol value'
}

var forIn = []
for (let x in obj) {
  forIn.push(x)
}

var forOf = []
for (let x in obj) {
  forOf.push(x)
}

Object.keys(obj).includes(sml) // false
Object.getOwnPropertyNames(obj).includes(sml) // false
forIn.includes(sml) // false
forOf.includes(sml) // false

Object.getOwnPropertySymbols(obj).includes(sml) // true
複製代碼

咱們看到上面提到的幾個方法都是沒法獲取到 Symbol值的。因此, ECMAScript提供了Object.getOwnPropertySymbols方法來遍歷實例屬性是Symbol的對象。
咱們繼續看一下這個方法對不可枚舉屬性以及原型對象屬性的遍歷:spa

var ctor = function() {}
var makeSml = key => Symbol(key)
// 原型對象
Object.defineProperties(ctor.prototype, {
  [makeSml('y1')]: {
    value: 2,
    enumerable: true
  },
  [makeSml('y2')]: {
    value: 22,
    enumerable: false
  }
})
var obj = new ctor()

// 設置實例對象屬性
Object.defineProperties(obj, {
  [makeSml('z1')]: {
    value: 3,
    enumerable: true
  },
  [makeSml('z2')]: {
    value: 33,
    enumerable: false
  }
})

Object.getOwnPropertySymbols(obj) // [Symbol(z1), Symbol(z2)]
複製代碼

咱們仍是用表格展現一下:prototype

方法 實例屬性 原型對象屬性
可枚舉(z1) 不可枚舉(z2) 可枚舉(y1) 不可枚舉(y2)
Object.getOwnPropertySymbols × ×


因此, Object.getOwnPropertySymbols僅能夠獲取到實例屬性爲 Symbol的屬性值。
設計

總結

因爲 ECMAScript版本不斷更新發布,這門語言會在兼容一下老的設計和新的設計中變得愈來愈複雜、臃腫。簡單的一個獲取對象屬性值,卻提供了好幾個方法。而且,有時候你還要組合使用它們才能達到你的需求,好比你要獲取一個對象的全部實例屬性值,不管普通仍是 symbol,那麼你可能就要組合使用 Object.getOwnPropertyNamesObject.getOwnPropertySymbols,若是你只取可枚舉的普通實例屬性值,那麼你還要取Object.getOwnPropertyNamesObject.keys的交集。

在通常的業務場景中,咱們可能用不到這些方法,不少時候,一個Object.keys就能知足需求。但在構建一些基礎庫(例如 lodash),或者框架的時候(例如 vue)時,咱們必須考慮到這些。

參考

  1. Object.keys
  2. Object.getOwnPropertyNames
  3. for...in
  4. for...of