# 防篡改对象

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

与密封和不允许扩展一样,对冻结的对象执行非法操作在非严格模式下会被忽略,而在严格模式下 会抛出错误。

最后更新时间: 2/21/2022, 3:29:09 PM