Javascipt中如果想使得对象或属性不能被修改,有很多种方式,下面由对象冻结的深浅程度,依次记录(由浅入深)。
1. Object.preventExtensions:不能增
Object.preventExtensions()方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
let obj = {}
Object.preventExtensions(obj)
obj.name = 'liderder'
console.log(obj);
我们可以看到,obj中没有添加name属性。
同时,如果使用Object.defineProperty来增加对象属性,则会抛出异常:
let obj = {}
Object.preventExtensions(obj)
Object.defineProperty(obj, "age", {
value: 21
})
并且,不可扩展对象的原型是不可变的,如果修改会抛出异常:
let obj = {}
Object.preventExtensions(obj)
obj.__proto__ = {name:'liderder'}
那么我们是否可以修改对象的原有属性呢?答案是可以的
let obj = {
name: 'liderder'
}
Object.preventExtensions(obj)
obj.name = 'li'
console.dir(obj);
此时,我们想知道既然修改原有属性是可行的,那么删除这个属性呢?也是可以的!
let obj = {
name: 'liderder'
}
Object.preventExtensions(obj)
delete obj.name
console.dir(obj);
总的来说 使用Object.preventExtensions()可以使一个对象不能够增加新的属性,但是可以对原有属性进行修改和删除操作。可以说这种方法冻结的不够存粹,那么有没有更纯粹的方法呢?就是下面这个
2. Object.seal():不能增删
Object.seal()方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
使用这个方法冻结的对象,不可以增加新的属性,可以修改原有的属性,这些和Object.preventExtensions()一样,但是既然说它比Object.preventExtensions()更加纯粹,肯定有不一样的地方——Object.seal()还可以禁止删除原有属性。
与Object.preventExtensions()一样的地方就不做展示了,这里只做不一样的展示:
let obj = {
name: 'liderder'
}
Object.seal(obj)
delete obj.name
console.log(obj);
但是 虽然实现了对象属性的不可新增,不可删除,但是上面这两种方法都不能实现对象原有属性的不可修改,那么有没有比Object.seal()更加纯粹的冻结方法呢?
也是有的!
3. Object.freeze():不能增删改
使用Object.freeze()冻结的对象中的现有属性值是不可变的。用Object.seal()密封的对象可以改变其现有属性值。
let obj = {
name: 'liderder'
}
Object.freeze(obj)
obj.name = 'li'
console.log(obj);
我们可以发现,name属性的值并没有被删除掉。Object.freeze()方法使得被冻结的对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。
但是实际上,以上的三种方法都由一个弊端,
可以通过改变原型对象,来为对象增加属性。【除非把obj的原型也冻结住】
如果属性值是对象,上面这些方法只能冻结属性指向的对象,而不能冻结对象本身的内容。
let obj = {
name: 'liderder',
like: ['coding', 'eating', 'playing']
}
Object.freeze(obj)
obj.like.push('liderder')
console.log(obj);
我们可以看到,通过往obj.like中push新元素,是可以的。也就是说我们冻结的仅仅是like中保存的地址,但是这块地址指向的堆内存我们是可以修改的。还有就是在原型上新增属性,obj也是可以访问到的。
let obj = {
name: 'liderder',
}
Object.freeze(obj)
obj.__proto__.age = 13
console.log(obj);
那么有没有一种方法可以实现这样的一个需求,我要让一个对象彻底的冻结应该怎么办呢?
4. 手动实现一个冻结函数
function deepFreeze(obj) {
if (typeof obj != 'object') {
throw Error('Type Error!')
}
Object.freeze(obj);
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
if (typeof obj[key] === 'object') {
deepFreeze(obj[key])
}
}
}
}
调用方法:
let obj = {
name: 'liderder',
like: ['cat', 'dog', 'bat']
}
deepFreeze(obj)
obj.like[2] = 'li'
console.log(obj);
打印结果:
nice!(但是我们仍然可以通过向Object的显示原型对象上增加属性,并且通过obj访问。。。)