类库开发或特别地对外暴露的接口对象不允许向上面随意添加,删除,修改方法、属性时需要使用对象防篡改
对象防篡改目前有 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
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
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 // 失败
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]] 只对象数据属性生效,对访问器属性无效
# 副作用
- Object.preventExtensions/seal/freeze 禁止对象重写原型对象
obj.__proto__ = {} // 失败
- Object.seal/freeze 设置 [[configurable]] 为 false,导致设置 [[enumerable]] 值时报错
- Object.freeze 对访问器写操作无法禁止,所以 Object.isSealed 结果也是 true
# 深度递归防篡改
防篡改只限制自身,而且只限制指定属性,不限制嵌套引用属性
let obj = {a: {b: 2}}
Object.freeze(obj)
obj.a = 100 // 失败,只限制 a 自身
obj.a.b = 100 // ok, 不限制嵌套 b 属性
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);
}
2
3
4
5
6
7
8
9
10
# 小结
--- | 添加属性 | 删除属性 | 修改属性 | [[enumerable]] 描述符属性可修改 | 对象原型是否可重写 |
---|---|---|---|---|---|
Object.preventExtensions | 否 | 是 | 是 | 是 | 否 |
Object.seal | 否 | 否 | 是 | 否 | 否 |
Object.freeze | 否 | 否 | 否 (如果是访问器属性,set 函数依然可调用 | 否 | 否 |
扫一扫,微信中打开