对象防篡改

类库开发或特别地对外暴露的接口对象不允许向上面随意添加,删除,修改方法、属性时需要使用对象防篡改

对象防篡改目前有 3 种方法:Object.preventExtensions/seal/freeze,都是 Object 静态方法,返回值是原对象引用

防篡改限制等级是:preventExtensions < seal < freeze

同时分别提供了 Object.isExtensible/isSealed/isFrozen 判断对象是否被限制

只限制对象自身,而且是浅限制,不深度递归限制,另外不限制原型

功能限制:属性添加、删除、修改(赋值或属性描述符对象)

严格模式:报错,非严格模式下静默失败

传入非对象类型参数:ES5 则报错,ES6 则静默返回传入的参数

# Object.preventExtensions

对象禁止扩展,即禁止对象上添加属性

对象属性可删除、修改

let obj = {a: 1, b: 2}

Object.preventExtensible(obj)

Object.isExtensible(obj) // true

obj.c = 3 // 新增属性 c 非严格模式,静默失败,严格模式:Cannot add property c, object is not extensible

delete obj.a // ok

obj.a = 100 // ok
1
2
3
4
5
6
7
8
9
10
11

# Object.seal

对象密封,即禁止删除对象属性,是在 Object.preventExtensions 基础上

对象属性可修改

let obj = {a: 1, b: 2}

Object.seal(obj)

Object.isSealed(obj) // true

obj.c = 3 // 失败

delete obj.b // 失败

obj.a = 100 // ok
1
2
3
4
5
6
7
8
9
10
11

# Object.freeze

对象冻结,即禁止修改对象属性,是在 Object.seal 基础上

对象数据属性不可修改,即不可重新赋值,访问器属性正常调用

因为访问器属性 set 可正常调用,所以 Object.isSealed 返回值也是 true

let obj = {a: 1, b: 2}

Object.freeze(obj)

Object.isFroze(obj) // true

Object.isSealed(obj) // true

obj.c = 3 // 失败

delete obj.b  // 失败

obj.a = 100 // 失败
1
2
3
4
5
6
7
8
9
10
11
12
13

# 原理

Object.preventExtensions 禁止扩展,未知,估计有一个类似 [[extensible]] 属性

Object.seal 描述符对象 [[configurable]] 为 false

Object.freeze 描述符对象 [[configurable]]、[[writable]] 描述符对象设置为 false,其中 [[writable]] 只对象数据属性生效,对访问器属性无效

# 副作用

  1. Object.preventExtensions/seal/freeze 禁止对象重写原型对象 obj.__proto__ = {} // 失败
  2. Object.seal/freeze 设置 [[configurable]] 为 false,导致设置 [[enumerable]] 值时报错
  3. Object.freeze 对访问器写操作无法禁止,所以 Object.isSealed 结果也是 true

# 深度递归防篡改

防篡改只限制自身,而且只限制指定属性,不限制嵌套引用属性

let obj = {a: {b: 2}}

Object.freeze(obj)

obj.a = 100 // 失败,只限制 a 自身

obj.a.b = 100 // ok, 不限制嵌套 b 属性
1
2
3
4
5
6
7
function deepFreeze(obj) {
  const propNames = Object.getOwnPropertyNames(obj);
  propNames.forEach(function(name) {
    let prop = obj[name];

    if (typeof prop == 'object' && prop !== null)
      deepFreeze(prop);
  });
  return Object.freeze(obj);
}
1
2
3
4
5
6
7
8
9
10

# 小结

--- 添加属性 删除属性 修改属性 [[enumerable]] 描述符属性可修改 对象原型是否可重写
Object.preventExtensions
Object.seal
Object.freeze 否 (如果是访问器属性,set 函数依然可调用

扫一扫,微信中打开

微信二维码