1 . es6語法中的常量聲明符 constjavascript
const freeze = 'strange' freeze = 'tony' // => Uncaught TypeError: Assignment to constant variable.
若是const聲明一個對象會如何?java
const freezeHero = { name: 'strange', skill: 'magic' } freezeHero = { name: 'no' } // => Uncaught TypeError: Assignment to constant variable. // 改變該對象的屬性 freezeHero.name = 'tony' freezeHero.skill = 'equip' console.log(freezeHero) // => {name: 'tony', skill: 'equip'}
const聲明的對象屬性仍然能夠改變,由於僅僅只是變量指向的那個內存地址不能改動。es6
2 . Object.freeze()api
Object.freeze()
一樣也是es6新增的apiapp
const freezeMan = { name: 'tony' } Object.freeze(freezeMan) freezeMan.name = 'strange' freezeMan.skill = 'magic' console.log(freezeMan) // => {name: 'tony'}
能夠看到,對象的靜態屬性變爲只讀,不可修改,且不能夠添加新屬性,若是屬性自己也是對象會如何?ui
const freezeMen = { members: ['tony', 'strange'], level: 2 } Object.freeze(freezeMen) freezeMen.level = 4 // 修改對象的members屬性 Array.prototype.push.apply(freezeMen.members, ['captain', 'hulk']) console.log(freezeMen) // => {members: ['tony', 'strange', 'captain', 'hulk'], level: 2}
被鎖定的對象,屬性值爲簡單類型時會被freeze,但值爲對象時仍然能夠修改,這與const聲明符的原理一致。下面經過遞歸的方式,實現對象引用的深層次鎖定,對象的任何屬性都不可重寫,也不可動態添加新屬性prototype
const freezeMen = { members: ['tony', 'strange'], level: 2 } const deepLock = function(obj){ Object.freeze(obj) Object.keys(obj).map((k, i) => { if(typeof obj[k] === 'object'){ deepLock(obj[k]) } }) return obj } deepLock(freezeMen).members = ['captian', 'hulk'] freezeMen.victory = true console.log(freezeMen) // => {members: ['tony', 'strange'], level: 2} // 若是再想經過defineProperty方法來增長新屬性,會直接拋出異常 Object.defineProperty(freezeMen, 'lastDefine', { writable: false, value: 'it is lastDefine', enumerable: true }) // => Uncaught TypeError: Cannot define property lastDefine, object is not extensible
3 . Object.definePropertycode
用這個方法實現的效果與freeze方法差很少,設置writable屬性值爲只讀,對於簡單值類型有效,而屬性值自己爲對象時仍然是能夠修改其值的。一樣能夠使用遞歸來實現對象
var lockProperty = function(data) { if(typeof data === 'object') { Object.keys(data).map(key => { defineDisWritable(data, key, data[key]) }) } return data } var defineDisWritable = function(obj, key, val) { Object.defineProperty(obj, key, { writable: false, value: val, enumerable: true }) if(typeof val === 'object') { lockProperty(val) } } const freezeMen = { members: { people: { name: 'default' } }, level: 2 } lockProperty(freezeMen) freezeMen.add = 'new key' freezeMen.level = 10 freezeMen.members = { house: 'big' } freezeMen.members.people.name = 'modified' console.log(freezeMen) // => {add: 'new key', members: {people: {name: 'default'}, level: 2} // 咱們試試使用defineProperty添加新屬性 Object.defineProperty(freezeMen, 'lastkey', { writable: false, value: 'last', enumerable: true }) console.log(freezeMen) // => {add: 'new key', members: {people: {name: 'default'}, level: 2, lastkey: 'last'}
上述方法也能夠實現對象深層嵌套的屬性凍結,與Object.freeze()
的惟一區別是,傳遞的頂層對象仍然能夠添加新的屬性(無論是經過動態添加仍是Object.defineProperty
)。遞歸
還能夠經過劫持setter來鎖定經過defineProperty方法添加的屬性。
var lockProperty = function(data) { if(typeof data === 'object') { Object.keys(data).map(key => { defineDisWritable(data, key, data[key]) }) } return data } var defineDisWritable = function(obj, key, val) { Object.defineProperty(obj, key, { set: function(newVal) { // 不賦新值 // val = newVal }, get: function() { return val }, enumerable: true }) if(typeof val === 'object') { lockProperty(val) } } const freezeMen = { members: { people: { name: 'default' } }, level: 2 } lockProperty(freezeMen) freezeMen.add = 'new key' freezeMen.level = 10 freezeMen.members = { house: 'big' } freezeMen.members.people.name = 'modified' console.log(freezeMen) // => {add: 'new key', members: {people: {name: 'default'}, level: 2}
_比較Object.defineProperty()
和Object.freeze()
兩種方法的遞歸方案,對於複雜的數據對象,能夠實現兩種狀況:
1.要存儲一個徹底不可寫的數據,使用Object.freeze();
2.要存儲一個不可修改但可拓展的數據,使用Object.defineProperty()