# 防篡改对象
Javascript 共享的本质一直是开发人员心头的痛。因为任何对象都可以被在同一环境中运行的代码修改。开发者们很可能会意外的修改别人的代码,甚至更糟糕地,用不兼容的功能重写原生对象。
在 JavaScript 中,为了解决这个问题,可以让开发人员定义 防篡改对象
在给对象设置属性时,可以手动设置 每个属性 的 [[Configurable]]、[[Writable]]、[[Enumerable]]、[[Value]] 、[[Get]] 以及 [[Set]] 特性,以改变属性的行为
TIP
对象的 属性 特性
[[Configurable]]: 表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能够把数学修改为访问器属性。默认为 true
[[Enumerable]]: 表示能够通过 for - in 循环返回属性。 默认为true
[[Writable]]: 表示能够修改属性的值。 默认为 true
[[Value]]:包含这个属性的数据值。读取对象属性值的时候,从这个位置读;在写入属性值的时时候,把新值保存在这个位置。 默认值为 undedined
[[Get]]: 在读取属性时调用的函数。默认值为 undefined
[[Set]]: 在写入属性时调用的函数。 默认值为 undfined
var book = {
_year:2004,
edition:1
}
Object.defineProperty(book,'year',{
get:function(){
return this._year
},
set:function(newVal){
if(newVal > 2020 ){
this._year = newVal
this.edition += newVal - 2020
}
}
})
book.year = 2022
console.log(book) // {edition:3 , _year:2022, year:2022 }
类似的 ES5 也增加了几个方法 ,通过它们可以指定对象的行为。不过注意: 一旦把对象京以为防篡改,就无法撤销了。
# 不可扩展对象(Object.preventExtensios)
默认情况下,所有对象都是可以扩展的。也就是说,任何时候都可以向对象中添加属性和方法。但 ES5 为对象提供了一个方法Object.preventExtensions(objName)
一旦通过 该方法 改变 对象 可以扩展的 这个行为后,就无法再像该对象中添加属性和方法
ES5 还提供了一个方法用来检测对象是否被禁止篡改: Object.isExtensible(objName)
var person = { name: "Nicholas" }
person.age = 18
console.log(person.age) // 18
console.log(Object.isExtensible(person)) // true
// 防篡改处理
Object.preventExtensions(person)
person.sex = 1
person.age = 20
console.log(person.sex) // undefined
console.log(person.age) // 20
console.log(Object.isExtensible(person)) // false
在调用了 Object.preventExtensions()
方法后,就不能给 person
对象添加新属性和方法了,但仍然能够修改设置和删除 已存在的属性和方法。在非严格模式下,给对象添加新成员会导致静默失败,因此 person.age
将是 undefined
。而在严格模式下,尝试给不可扩展的对象添加新成员会导致抛出错误。也不能使用Object.defineProperty()
把添加属性。
# 密封的对象
ES5 为对象定义的第二个保护级别是密封对象(sealed object).
密封对象: 不可扩展,且已有成员的 [[Configurable]] 特性将被设置为 false。这也就意味着不能删除属性和方法,因为不能使用 Object.defineProperty()
把数据修改为访问器属性,或者相反。但已有成员的属性值是可以修改的。
密封方法:Object.seal(objName)
使用 Object.isSealed(objName)
可以检测对象是否被蜜蜂了。因为密封的对象不可扩展,所以用Object.isExtensible()
检测密封的对象也会返回false.
var person = { name:'zs' }
console.log(Object.isSealed(person)) // false
console.log(Object.isExtensible(person)) //true
Object.seal(person)
person.age = 18
person.name = 'ls'
console.log(person.age) // undefined
console.log(person.name) // ls
console.log(Object.isSealed(person)) // true
console.log(Object.isExtensible(person)) //false
delete person.name
console.log(person.name)
对象被密封后,在严格模式下,增删密封对象属性都会抛出错误。非严格模式下,忽略增删操作。
# 冻结的对象
最严格的防篡改级别是 冻结对象。 被冻结的对象既不可扩展,优势密封的,而且对象数据属性的[[Writable]]特性将会被设置为false,即已有成员的数据值不可修改。如果定义[[Set]]函数,访问器属性仍然是可写的。
检测一个对象是否是冻结对象,可以使用 Object.isFrozen()
检测
var person = {name:'zs'}
Object.freeze(person)
person.age = 29
console.log(person.age) // undefined
person.name = 'ls'
console.log(person.name) // 'zs'
delete person.name
console.log(person.name) // 'zs'
console.log(Object.isExtensible(person)) // false
console.log(Object.isSealed(person)) // true
console.log(Object.isFrozen(person)) // true
与密封和不允许扩展一样,对冻结的对象执行非法操作在非严格模式下会被忽略,而在严格模式下 会抛出错误。