保存常量,使其只可读,实现方式有哪些
1 . es6语法中的常量声明符 const
const freeze = 'strange'
freeze = 'tony' // => Uncaught TypeError: Assignment to constant variable.
如果const声明一个对象会如何?
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声明的对象属性仍然可以改变,因为仅仅只是变量指向的那个内存地址不能改动。
2 . Object.freeze()
Object.freeze()
同样也是es6新增的api
const freezeMan = {
name: 'tony'
}
Object.freeze(freezeMan)
freezeMan.name = 'strange'
freezeMan.skill = 'magic'
console.log(freezeMan) // => {name: 'tony'}
可以看到,对象的静态属性变为只读,不可修改,且不可以添加新属性,如果属性本身也是对象会如何?
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声明符的原理一致。下面通过递归的方式,实现对象引用的深层次锁定,对象的任何属性都不可重写,也不可动态添加新属性
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.defineProperty
用这个方法实现的效果与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()
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。