跳至主要内容

[JS] preventExtensions & seal & freeze

TL;DR

defineProperty可以設定物件個別屬性的特徵。而本章節介紹的preventExtensions,freeze,seal則是調整整個物件的特徵(e.g:物件的屬性是否可寫入、物件本身是否可擴充...etc)。

參考資料

相關連結


preventExtensions

結論:設定為防止擴充,只會讓整個物件都沒辦法再透過任何方式新增屬性。其餘如調整個別屬性特徵、刪除屬性、更改屬性值等等都不會有任何問題

語法

Object.preventExtensions(obj)

驗證

Object.isExtensible(obj)

範例

const foo={
a:1,
b:2,
c:{}
}

console.log(Object.isExtensible(foo)) // true

Object.preventExtensions(foo);

console.log(Object.isExtensible(foo)) // false



foo.d=4; // 不會跳出錯誤,但是也不會增加d屬性
console.log(foo); // {a: 1, b: 2, c: {}}

foo.c.ca='ca'; // 巢狀物件可以寫入值
console.log(foo); // {a: 1, b: 2, c: {ca:'ca'}}

console.log('調整a的configurable之前',Object.getOwnPropertyDescriptor(foo,'a'));
// 調整a的configurable之前 {value: 1, writable: true, enumerable: true, configurable: true}

Object.defineProperty(foo,'a',{
configurable:false
})

console.log('調整a的configurable之後',Object.getOwnPropertyDescriptor(foo,'a'));
// 調整a的configurable之後 {value: 1, writable: true, enumerable: true, configurable: false}

delete foo.b // true

console.log(foo); // {a: 1, c: {ca:'ca'}}
caution
const foo={
a:1,
b:2,
c:{}
}
Object.preventExtensions(foo);
Object.defineProperty(foo,'e',{
value:'透過defineProperty新增的屬性'
})
// TypeError: Cannot define property e, object is not extensible

已設定為不可擴展(preventExtensions)的物件,如果使用defineProperty去擴充屬性,會直接跳出錯誤

seal

結論:除了不可擴展之外(即無法新增),另外所有的屬性特徵都會改為configurable:false

info

一個 被seal 的物件,使用isExtensible去驗證是否被防止擴展時,也會是true。

語法

Object.seal(obj)

驗證

Object.isSealed(obj)

範例

const foo={
a:1,
b:2,
c:{}
}

Object.seal(foo);

console.log(Object.isExtensible(foo)); // false
console.log(Object.isSealed(foo)); // true
/* 驗證只要被seal之後,同時也會被preventExtensions */

foo.a=3;
/* 可以重新配置屬性的值 */
console.log(foo); // {a: 3, b: 2, c: {}}

foo.d=4
/* 無法新增屬性(因為已經被preventExtensions了),但是非嚴格模式下並不會跳出錯誤 */
console.log(foo); // {a: 3, b: 2, c: {}}

delete foo.b; // false
/* 因為seal會將所有屬性改為configurable:false,所以無法刪除 */

foo.c.ca='ca'
/* 只能限制第一層物件,無法限制深層巢狀物件 */
console.log(foo); // {a: 3, b: 2, c: {ca:'ca'}}

console.log(Object.getOwnPropertyDescriptor(foo,'a'));
// {value: 3, writable: true, enumerable: true, configurable: false}
console.log(Object.getOwnPropertyDescriptor(foo,'b'));
// {value: 2, writable: true, enumerable: true, configurable: false}

Object.defineProperty(foo,'a',{
writable:false
})
console.log(Object.getOwnPropertyDescriptor(foo,'a'));
// {value: 1, writable: false, enumerable: true, configurable: false}
/* 因為configurable是false的狀態下(seal自動將所有屬性轉為不可調整),只能將writable從true改為false */

Object.defineProperty(foo,'a',{
enumerable:false
})
// Uncaught TypeError: Cannot redefine property: a
/* 因為嘗試調整enumerable,但是configurable已經是false了,所以會造成錯誤 */

freeze

** 結論:除了seal的特性以外,另外將所有屬性特徵都調整為不可寫入(writable:fasle) **

語法

Object.freeze(obj)

驗證

Object.isFrozen(obj)

範例

const foo={
a:1,
b:2,
c:{}
}

console.log('freeze之前',Object.getOwnPropertyDescriptor(foo,'a'));
// freeze之前 {value: 1, writable: true, enumerable: true, configurable: true}

Object.freeze(foo);

console.log('freeze之後',Object.getOwnPropertyDescriptor(foo,'a'));
// freeze之後 {value: 1, writable: false, enumerable: true, configurable: false}

console.log(Object.isExtensible(foo)); // false
console.log(Object.isSealed(foo)); // true
console.log(Object.isFrozen(foo)); // true

try {
Object.defineProperty(foo,'a',{
writable:true
})
} catch (error) {
console.log(error); // TypeError: Cannot redefine property: a
}

總結

方法名稱isExtensibleisSealedisFrozenWritableConfigurableEnumerable
preventExtensionsXXXOOO
sealXOXOXO
freezeXOOXXO