ECMAScript 5 新特性 vol.3 - Object

封裝

讓咱們回顧下在ES3中定義簡單對象的方式:vue

var man = {
    name: 'kid',
    age: 18
  }

很是簡潔明瞭,但開發者缺少更多的控制權。好比,我但願某個屬性只讀,或者成爲私有屬性。爲此,ES5提供了一些可用機制,協助你封裝對象。數組

配置項

Object.defineProperty方法爲對象定義屬性,並附帶一些可選項:code

var man = {}
  Object.defineProperty(man, 'name', {
    value: 'kid',
    writable: true,
    enumerable: true,
    configurable: true
  })
  console.log(man.name)  // -> 'kid'

配置項做用以下:對象

  • writable - 屬性是否可寫繼承

  • enumerable - 屬性是否可在for-in中出現接口

  • configurable - 屬性是否能夠被從新定義ip

這三個配置的默認值都爲false,因此當你不配置時,生成的就是一個只讀的私有屬性原型鏈

var man = {}
  Object.defineProperty(man, 'name', {
    value: 'kid',
  })
  
  man.name = 'wumeng'    // 不可寫,Strict模式下會拋出異常
  console.log(man.name)  // -> 'kid'
  
  for( var key in man ){
    console.log( man[key] )  // man.name不會被遍歷到
  }
  
  // 不能再次定義,即便只是修改配置也不行,Strict模式下會拋出異常
  Object.defineProperty(man, 'name', {
    writable: true
  })

屬性訪問器( Getter / Setter )

訪問器將真正存值的變量隱藏起來,並提供對外的接口,當你讀取或設置屬性時,可執行必定的操做:開發

var birthyear = 1989
  var man = {}
  Object.defineProperty(man, 'age', {
    get: function(){
      var now = new Date()
      return now.getFullYear() - birthyear
    },
    set: function( age ){
      var now = new Date()
      birthyear = now.getFullYear() - age
    }
  })

  // 假設當前爲2015年
  console.log(man.age)    // -> 26
  man.age = 18
  console.log(birthyear)  // 1997
  console.log(man.age)    // 18

上列中set的參數就是你設置的值。從外部看,你設置man.age的方式與賦值無異,這與el.style.color = "red"類似:看上去是簡單的賦值,卻觸發了必定行爲(設置DOM文本爲紅色)。get

訪問器在其餘語言(如C#)中很常見,是很好用的特性,由於它讓MVVM模式擁有一種十分便捷的數據綁定(Model->View)方式,可參見Vue.js,在此不展開。

注意,若是你用了訪問器,則不能再設置valuewritable配置項。

多重定義

你能夠用Object.defineProperties方法同時定義多個屬性:

var man = {}
  Object.defineProperties(man, {
    name: {
      value: 'kid',
      writable: false
    },
    age: {
      get: function(){},
      set: function(age){}
    }
  })

讀取值與配置項

Object.getOwnPropertyDescriptor方法可讀取defineProperty設定的值、配置項、訪問器:

var man = {}
  Object.defineProperty(man, 'name', {
    value: 'kid',
    writable: true,
    enumerable: true,
    configurable: true
  })
  
  var desc = Object.getOwnPropertyDescriptor(man, 'name')
  console.log(desc)
  
  // ->
  // Object {
  //   value: 'kid',
  //   writable: true,
  //   enumerable: true,
  //   configurable: true
  // }

禁止擴展

Object.preventExtensions方法禁止對象添加新屬性:

var man = {
    name: 'kid'
  }
  Object.preventExtensions(man)
  man.age = 18  // Strict模式下拋出異常

經過Object.isExtensible方法能夠檢驗對象是否能夠擴展:

var man = {
    name: 'kid'
  }
  Object.isExtensible(man)  // true
  Object.preventExtensions(man)
  Object.isExtensible(man)  // false

密封

Object.seal方法用來密封一個對象,這比禁止擴展更嚴格一些,根據MDN Object.seal的描述,對象一旦被密封,則不可增長、刪除其屬性,也不可配置或定義訪問器。若在Strict模式下違反上述原則,將拋出異常。但在Chrome中實測,仍可配置writableconfigurable項,緣由未知。:

var man = {
    name: 'kid'
  }
  Object.seal(man)

  man.age = 18     // 不能添加屬性,拋出異常
  delete man.name  // 不能刪除屬性,拋出異常
  
  Object.defineProperty(man, 'name', {
    enumerable: false,  // 不能配置enumerable,拋出異常
    get: function(){},  // 不能轉換爲訪問器,拋出異常
    set: function(){}   // 不能轉換爲訪問器,拋出異常
  })

isSealed可檢查一個對象是否密封:

var man = {
    name: 'kid'
  }
  
  Object.isSealed(man)  // -> false
  Object.seal(man)
  Object.isSealed(man)  // -> true

凍結

密封對象尚可修改屬性值,而凍結對象連這也不行,可謂最嚴格的封裝,近似於常量:

var man = {
    name: 'kid'
  }
  Object.freeze(man)

  man.name = 'wumeng'  // 不能修改屬性,拋出異常
  man.age = 18         // 不能添加屬性,拋出異常
  delete man.name      // 不能刪除屬性,拋出異常
  
  Object.defineProperty(man, 'name', {
    writable: true,      // 不能配置爲true,拋出異常
    enumerable: true,    // 不能配置爲true,拋出異常
    configurable: true,  // 不能配置爲true,拋出異常
    get: function(){},   // 不能轉換爲訪問器,拋出異常
    set: function(){},   // 不能轉換爲訪問器,拋出異常
  })

但必須注意,freeze只能實現淺凍結,即僅僅凍結子屬性。若子屬性是一個對象,好比一個數組,那麼它的子屬性依然不受限制:

var man = {
    hobbies: ['game','comic','music']
  }
  Object.freeze(man)
  man.hobbies[1] = 'code'
  console.log(man.hobbies)  // -> ['game','comic','music']

Object.isFrozen方法檢測一個對象是否被凍結:

var man = {
    name: 'kid'
  }
  
  Object.isFrozen(man)  // -> false
  Object.freeze(man)
  Object.isFrozen(man)  // -> true

讀取屬性名

ES5還新增了兩個方法來獲取對象屬性的key名:

Object.keys返回一個數組,包含對象的屬性名,但會忽略不可枚舉的屬性:

var man = {
    name: 'kid',
    age: 18
  }
  
  Object.defineProperty(man, 'job', {
    value: 'coder',
    enumerable: false
  })
  
  Object.keys(man)  // -> ['name','age']

與此相對,Object.getOwnPropertyNames方法可得到全部屬性名,包括不可枚舉的:

var man = {
    name: 'kid',
    age: 18
  }
  Object.defineProperty(man, 'job', {
    value: 'coder',
    enumerable: false
  })
  
  Object.getOwnPropertyNames(man)  // -> ['name','age','job']

這兩個方法都不會包含從原型鏈上繼承的屬性。

原創,自由轉載,請署名,本人博客 kid-wumeng.me

相關文章
相關標籤/搜索