讓咱們回顧下在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 })
訪問器將真正存值的變量隱藏起來,並提供對外的接口,當你讀取或設置屬性時,可執行必定的操做:開發
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,在此不展開。
注意,若是你用了訪問器,則不能再設置value
與writable
配置項。
你能夠用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中實測,仍可配置writable
與configurable
項,緣由未知。:
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