物件屬性不可寫入?物件擴充的修改與調整

前言

在先前有介紹到如果要調整屬性的特徵我們可以使用 Object.defineProperty 與 Object.defineProperties 這兩個物件的方法。那麼接下來我們要來介紹如何讓物件可以防止物件擴張、封裝物件與凍結物件的方法,這些方法分別為:

  • Object.preventExtensions() 防止物件擴張
  • Object.seal() 封裝物件
  • Object.freeze() 凍結物件

Object.preventExtensions()

Object.preventExtensions() 的作用為防止擴充,透過下方的程式碼範例可以了解,對物件使用 preventExtensions 的時候,依舊可以調整屬性值,也可以修改屬性特徵,能夠刪除屬性,不過無法新增新的屬性。

// preventExtensions
// 防止擴充
Object.preventExtensions(person);
console.log('是否可被擴充', Object.isExtensible(person));
console.log('person a 的屬性特徵', Object.getOwnPropertyDescriptor(person, 'a'));

// 調整屬性
person.a = 'a'; // 可調整屬性

// 新增屬性
person.d = 'd'; // 無法新增屬性

// 巢狀屬性調整
person.c.a = 'ca'; // 可以調整巢狀屬性

// 調整特徵
Object.defineProperty(person, 'a', {
configurable: false
});
// 可以修改屬性特徵

// 刪除
delete person.b; // 可以刪除屬性

// 結果
console.log('person 物件', person);
console.log('person a 屬性特徵(嘗試修改後)', Object.getOwnPropertyDescriptor(person, 'a'));

console 結果


Object.seal()

使用 Object.seal() 可以讓物件屬性無法新增刪除(巢狀屬性可以被調整),也無法重新配置特徵,但是可以調整目前屬性值。(該物件會被加上 preventExtensions)

var person = {
a: 1,
b: 2,
c: {}
}

// seal
// 物件屬性無法新增刪除,也無法重新配置特徵,但是可以調整目前屬性值
// 不過 writable 的屬性特徵可以調整為 false
// 物件會被加上 preventExtensions
Object.seal(person);
console.log('是否可被 封裝', Object.isSealed(person));
console.log('person a 的屬性特徵', Object.getOwnPropertyDescriptor(person, 'a'));

// 調整屬性
person.a = 'a'; // 可以調整屬性值

// 新增屬性
person.d = 'd'; // 無法新增屬性

// 巢狀屬性調整
person.c.a = 'ca'; // 可以調整巢狀屬性值

// 調整特徵
Object.defineProperty(person, 'a', {
writable: false // 若修改的屬性特徵為 writable,則可修改
});

// 刪除
delete person.b; // 無法刪除屬性

// 結果
console.log('person 物件', person);
console.log('person a 屬性特徵(嘗試修改後)', Object.getOwnPropertyDescriptor(person, 'a'));

console 結果

雖然說 Object.seal() 無法重新配置屬性特徵,不過在 MDN 的文件中有提到 writable 的屬性特徵可以調整為 false。

MDN 的文件中有提到 writable 的屬性特徵可以調整為 false

所以如果我們在使用 Object.seal() 的情況下嘗試修改物件中其他的屬性特徵,就會出現以下的錯誤訊息。

// 調整特徵
Object.defineProperty(person, 'a', {
enumerable: false // 無法修改該屬性特徵
});

在使用 Object.seal() 的情況下嘗試修改物件中其他的屬性特徵,出現的錯誤訊息。


Object.freeze()

再針對物件使用 Object.freeze() 的時候,無法新增、修改予刪除屬性值(巢狀屬性可以修改),無法修物件的屬性特徵。

var person = {
a: 1,
b: 2,
c: {}
}

// freeze
// 物件會加上 seal,並且無法調整值
Object.freeze(person);
console.log('是否被凍結', Object.isFrozen(person));
console.log('person a 的屬性特徵', Object.getOwnPropertyDescriptor(person, 'a'));

// 調整屬性
person.a = 'a'; // 無法調整屬性值

// 新增屬性
person.d = 'd'; // 無法新增屬性值

// 巢狀屬性調整
person.c.a = 'ca'; // 由於物件有一個傳參考特性,所以只能夠針對當下的物件作封裝,故巢狀屬性值是可以被調整的

// 刪除
delete person.b; // 無法刪除屬性值

// 結果
console.log('person 物件', person);
console.log('person a 屬性特徵(嘗試修改後)', Object.getOwnPropertyDescriptor(person, 'a'));

console 結果

在物件設定 Object.freeze() 的情況下調整屬性特徵,會跳出錯誤訊息並終止程式碼運行:

// 調整特徵
Object.defineProperty(person, 'a', {
configurable: true // 無法調整屬性特徵
});

物件設定 Object.freeze() 的情況下調整屬性特徵,會跳出錯誤訊息並終止程式碼運行


章節結論

  • 使用 Object.preventExtensions() 可以防止物件擴張,無法新增物件屬性。
  • 使用 Object.seal() 會封裝物件,使物件無法新增與刪除物件屬性(巢狀屬性可調整屬性值),不過可以調整屬性值;無法調整屬性特徵,除了 writable 可設定為 false 不再此限制。
  • 使用 Object.freeze() 會凍結物件,使物件無法新增、刪除、調整屬性值(巢狀屬性可調整屬性值),也無法調整屬性特徵。

參考文件