iT邦幫忙

0

Javascript 進階 7-2 物件屬性不可寫入?物件擴充的修改與調整

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

這篇文章呢要介紹 3 種 方法來針對整個物件進行調整喔!

分別是

1.preventExtensions => 防止擴充

2.seal => 封裝

3.Freeze => 凍結

那麼下面的是我們的範例物件

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

首先先讓我們介紹

preventExtensions 防止擴充

如同字面上的意義,就是要防止物件進行屬性的擴充(新增屬性),但是刪除或是重新賦予值都還是OK的~! 實際來看看怎麼應用吧!

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 屬性印出來看看。

那麼結果就如同下面所示:

https://ithelp.ithome.com.tw/upload/images/20200427/20121770GAXXRI8SMt.png

可以看到,因為執行了防止擴充的動作,所以是否可以被擴充的地方回傳的是false。

person物件的 a 屬性特徵則照實顯示,並且除了value以外其他三個都是true。

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

b屬性成功被刪除

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'));

結果如下:

https://ithelp.ithome.com.tw/upload/images/20200427/20121770LY7W7KeevR.png

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

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

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

b的屬性也還存在著。

同時,無法重新配置特徵就代表,利用 Object.defineProperty 將a屬性的 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'));

結果如下:

https://ithelp.ithome.com.tw/upload/images/20200427/20121770Qc9teJws4l.png

你會發現怎麼出錯了!!

原因就在於因為person物件被freeze了,所以這時候再進行Object.defineProperty的話就會直接抱錯,所以這邊我們要記得先把Object.defineProperty給註解調才能在正常運行喔!

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

註解後的結果:

https://ithelp.ithome.com.tw/upload/images/20200427/20121770atQrShx44s.png

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

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

當然b屬性也無法被刪除,d屬性無法被新增。

但巢狀賦值的c屬性還是可以不受限制。

以上就是關於 freeze的介紹。

以上三個方法都要再強調一遍,跟之前介紹的Object.defineProperty不同,是針對整個物件進行設定,但無法針對物件中的物件(巢狀物件)進行設定,要的話也是分別設定。

如果沒有問題的話,就往下一篇文章邁進吧!汪汪~


尚未有邦友留言

立即登入留言