防篡改
防止對象創(chuàng)建后,修改對象的結(jié)構(gòu)內(nèi)容
- 在相關(guān)級(jí)別的防篡改模式下,如果違背其設(shè)置要求,不會(huì)報(bào)錯(cuò),屬性對象不會(huì)改變
但是在嚴(yán)格模式('use strict')下,會(huì)報(bào)錯(cuò)- 在ES5中,如果設(shè)置防篡改的相關(guān)方法的參數(shù)不是一個(gè)對象(一個(gè)原始值),結(jié)果會(huì)導(dǎo)致 TypeError,而在ES2015中,非對象參數(shù)將被視為普通對象,并被簡單地返回。
三個(gè)級(jí)別:
1、防擴(kuò)展
禁止給對象添加任何新屬性(不可擴(kuò)展,可刪除,可修改),其本質(zhì)為:
Object.preventExtensions(obj),返回值為對象自身(引用)
var obj = { a: 1 };
// 對象初始的屬性描述符
console.log(Object.getOwnPropertyDescriptors(obj));
// a: { value: 1, writable: true, enumerable: true, configurable: true }
// 設(shè)置防擴(kuò)展后的對象屬性描述符
console.log(Object.getOwnPropertyDescriptors(obj));
// a: { value: 1, writable: true, enumerable: true, configurable: true }
可以看出,防擴(kuò)展,并沒有對對象的屬性描述符進(jìn)行二次修改
可以使用Object.isExtensible()判斷一個(gè)對象是否是可擴(kuò)展對象
Object.preventExtensions()能夠擴(kuò)展原型對象上的屬性
preventExtensions()只是針對對象中第一層的屬性進(jìn)行防擴(kuò)展,對于后面深層次的對象屬性無法進(jìn)行防擴(kuò)展
var obj = { a: 1, b: { name: "sun" } };
Object.preventExtensions(obj); // 設(shè)置防擴(kuò)展
obj.c = 100; // 為對象obj增加屬性 c
obj.b.age = 20; // 為obj中的b對象增加屬性 age
console.log(obj);
// { a: 1, b: { name: 'sun', age: 20 } }
// 屬性c添加失敗,而對obj中的b對象增加屬性 age 仍然有效
2、封閉
seal在防擴(kuò)展的基礎(chǔ)上,再禁止刪除現(xiàn)有的屬性(不可擴(kuò)展,不可刪除,可修改(writable為true的前提下)),其本質(zhì)為:
- 設(shè)置Object.preventExtension(),禁止添加新屬性
- 設(shè)置configurable為false,禁止更改配置
- 禁止更改訪問器屬性(getter和setter)
Object.seal(obj) 返回值為對象自身(引用)
var obj = { a: 1 };
// 對象初始的屬性描述符
console.log(Object.getOwnPropertyDescriptors(obj));
// a: { value: 1, writable: true, enumerable: true, configurable: true }
// 設(shè)置封閉后的對象屬性描述符
console.log(Object.getOwnPropertyDescriptors(obj));
// a: { value: 1, writable: true, enumerable: true, configurable: false }
可以看出,設(shè)置封閉之后,將對象的屬性描述符的configurable改為了false,
可以使用Object.isSealed()判斷一個(gè)對象是否是封閉對象
一個(gè)封閉對象,必然是一個(gè)不可擴(kuò)展對象,不可擴(kuò)展的空對象也是一個(gè)封閉對象
Object.seal()能夠修改原型對象上的屬性
freeze()是淺封閉(與淺拷貝類似),只是封閉了對象中第一層的屬性,對于后面的對象屬性無法進(jìn)行封閉
3、凍結(jié)
禁止對對象做任何修改(不可擴(kuò)展,不可刪除,不可修改),其本質(zhì)為:
- 設(shè)置Object.preventExtension(),禁止添加新屬性
- 設(shè)置writable為false,禁止修改值
- 設(shè)置configurable為false,禁止更改配置
- 禁止更改訪問器屬性(getter和setter)
Object.freeze(obj) 返回值為對象自身(引用)
var obj = { a: 1 };
// 對象初始的屬性描述符
console.log(Object.getOwnPropertyDescriptors(obj));
// a: { value: 1, writable: true, enumerable: true, configurable: true }
// 設(shè)置凍結(jié)后的對象屬性描述符
console.log(Object.getOwnPropertyDescriptors(obj));
// a: { value: 1, writable: false, enumerable: true, configurable: false }
可以看出,設(shè)置凍結(jié)對象,則對象的屬性描述符的writable、configurable都改為了false
可以使用Object.isFrozen()判斷一個(gè)對象是否是凍結(jié)對象
一個(gè)凍結(jié)對象,必然是一個(gè)封閉對象,不可擴(kuò)展空對象也是一個(gè)凍結(jié)對象
Object.freeze()能夠修改原型對象上的屬性
freeze()是淺凍結(jié)(與淺拷貝類似),只是凍結(jié)了對象中第一層的屬性,對于后面的對象屬性無法進(jìn)行凍結(jié)
實(shí)現(xiàn)深度防擴(kuò)展、深封閉、深凍結(jié)
- 深度防擴(kuò)展
Object.deepPreventExtensions = function (obj) {
// Object.keys無法獲取不可枚舉的屬性
// Object.getOwnPropertyNames獲取所有屬性
var _keys = Object.getOwnPropertyNames(obj);
if (_keys.length) {
_keys.forEach(k => {
var _value = obj[k];
// 如果值為對象,則繼續(xù)凍結(jié)
if (typeof _value === 'object' && _value !== null) {
Object.deepPreventExtensions(_value);
}
})
}
return Object.preventExtensions(obj);
}
- 深封閉
// 深度封閉
Object.deepSeal = function (obj) {
// Object.keys無法獲取不可枚舉的屬性
// Object.getOwnPropertyNames獲取所有屬性
var _keys = Object.getOwnPropertyNames(obj);
if (_keys.length) {
_keys.forEach(k => {
var _value = obj[k];
// 如果值為對象,則繼續(xù)凍結(jié)
if (typeof _value === 'object' && _value !== null) {
Object.deepSeal(_value);
}
})
}
return Object.seal(obj);
}
- 深凍結(jié)
// 深度凍結(jié)
Object.deepFreeze = function (obj) {
// Object.keys無法獲取不可枚舉的屬性
// Object.getOwnPropertyNames 能獲取所有屬性
var _keys = Object.getOwnPropertyNames(obj);
if (_keys.length) {
_keys.forEach(k => {
var _value = obj[k];
// 如果值為對象,則繼續(xù)凍結(jié)
if (typeof _value === 'object' && _value !== null) {
Object.deepFreeze(_value);
}
})
}
return Object.freeze(obj);
}