0%

JS核心-(53)-物件屬性延伸章節:屬性的特徵-物件屬性不可寫入?物件擴充的修改與調整

前言

針對整個物件進行調整的三種方法,防止擴充 preventExtensions、封裝 seal 、凍結 Freeze

上一篇文章我們提到了針對物件個別屬性進行調整的 Object.defineProperty 以及 Object.defineProperties 方法。

此篇是針對整個物件進行調整,這是需要特別注意的地方。


防止擴充 preventExtensions

如同字面上的意義,就是要防止物件進行屬性的擴充 (新增屬性) ,但是刪除或是重新賦予值都還是可以的。

我們實際來看看怎麼應用:

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

// preventExtensions
Object.preventExtensions(person);

// 針對物件是否可以被擴充進行檢查,回傳布林值
console.log('是否可被擴充', Object.isExtensible(person));

// 用於確認物件中特定的屬性是甚麼,回傳物屬性的4個特徵
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'));

首先,我們利用 Object.preventExtensions(person);person 這個物件進行防止擴充的動作。

所以我們可以利用 Object.isExtensible(person) 來檢查他是否可以擴充,這邊會回傳布林值來確認是否可以被擴充。

同時,如果我們想要知道某個物件的某個屬性的特徵的話,可以利用 Object.getOwnPropertyDescriptor(物件, 屬性名稱) 的方式

來查看,就會回傳一個物件包含該屬性的 4 個特徵。

所以這邊我們也可以在進行防止擴充的動作後檢查看看任一個該物件的屬性是否有被改變,再來就進行一連串的調整以及刪除之

後,把最後 person 物件以及 person a 屬性印出來看看。

結果就如同下面所示:

可以看到,因為執行了防止擴充的動作,所以是否可以被擴充的地方回傳的是 false,而 person 物件的 a 屬性特徵則照實顯

示,並且除了 value 以外其他三個都是true

但經過一連串的修改以後可以發現,person物件中,a 屬性的值被賦予成字串 ab 屬性成功被刪除,c 屬性的巢狀賦值

有成功,person物件的 a 屬性特徵其中 configurable 也成功被改為 false。唯獨新增 d 屬性是失敗的。

所以就如同一開始所描述的,防止擴充的動作其實就是在防止屬性的新增而已。


封裝 seal

seal 的效果是讓物件的屬性無法新增刪除,也無法重新配置特徵,但是可以調整前屬性值 (value)

同時,物件預設加上 preventExtensions

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

Object.seal(person);

// 針對物件是否被封裝進行檢查,回傳布林值
console.log('是否可被封裝', Object.isSealed(person));

// 用於確認物件中特定的屬性是甚麼,回傳物屬性的4個特徵
console.log('person物件的 a 屬性性質', Object.getOwnPropertyDescriptor(person, 'a'));

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

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

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

// 調整特徵
Object.defineProperty(person, 'a', {
    writable: false
});

// 刪除屬性
delete person.b;

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

結果如下:

所以在執行了封裝的動作之後,利用 Object.isSealed(person) 來檢查是否被封裝,當然就是 true

而你可以看到,還在還沒進行屬性的調整之前, a 屬性的特徵, configurable 就已經是 false 了,這也符合一開始說的

seal 的效果是讓物件的屬性無法新增刪除,也無法重新配置特徵。

b的屬性也還存在著。同時,無法重新配置特徵就代表,利用 Object.definePropertya 屬性的 writable 改為false 也是不成功的,結果也的確如此,a屬性的 writable 還是維持在 true 的結果。

d的屬性還是無法被新增,但巢狀賦值的執行還是成功的,c屬性的物件中還是有 a 屬性對應字串 ca 的值,而且a屬性還

是成功賦值成字串 a 的值。

以上就是關於 seal 的介紹。


凍結 Freeze

seal 很像,freeze 的物件無法新增刪除、不可以調整前屬性值(value),當然也也無法重新配置特徵。

同時,物件預設加上seal

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

Object.freeze(person);

// 針對物件是否被凍結進行檢查,回傳布林值
console.log('是否有被凍結', Object.isFrozen(person));

// 用於確認物件中特定的屬性是甚麼,回傳物屬性的4個特徵
console.log('person物件的 a 屬性性質', Object.getOwnPropertyDescriptor(person, 'a'));

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

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

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

// 調整特徵
Object.defineProperty(person, 'a', {
    enumerable: false
});

// 刪除屬性
delete person.b;

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

結果如下:

你會發現怎麼出錯了?

原因就在於因為 person 物件被 freeze 了,所以這時候再進行 Object.defineProperty 的話就會直接抱錯,所以這邊我們要記

得先把 Object.defineProperty 給註解調才能在正常運行。

// 調整特徵
// Object.defineProperty(person, 'a', {
//     enumerable: false
// });

註解後的結果:

我們來看,利用 Object.isFrozen(person) 來檢查是否有被凍結?當然是 true

再來 a 屬性的值不能被重新賦值,並且在 freeze 之後,只能夠被列舉, writable 以及 configurable 都是 false

當然 b 屬性也無法被刪除, d 屬性無法被新增。但巢狀賦值的 c 屬性還是可以不受限制。

以上就是關於 freeze 的介紹。

這邊要再次強調!

以上三個方法跟之前介紹的 Object.defineProperty 不同,是針對整個物件進行設定,但無法針對物件中的物件(巢狀物件)進行

設定,要的話也是分別設定。