前言
前一章節介紹了 Object.defineProperty,而這個語法主要是針對物件中的屬性做一些調整,因此這一篇將會介紹物件。
物件屬性不可寫入?物件擴充的修改與調整
如同前言,除了可以使用 Object.defineProperty 來針對物件中的屬性做操作之外,也可以針對物件本身做操作,而物件的操作主要是三個語法
Object.preventExtensions - 物件不可擴充
Object.seal - 物件封裝
Object.freeze - 物件凍結
接下來讓我們看一下所謂的 Object.preventExtensions 是什麼意思,首先這邊會有一個基本的程式碼:
1 2 3 4
| var obj = { myName: 'Ray', blog: {}, };
|
Object.preventExtensions 的使用方式非常的簡單,假設說我希望 obj 這一整個物件不可以再次擴充的話,只需要這樣寫即可:
1 2 3 4 5 6 7 8 9
| var obj = { myName: 'Ray', blog: {}, };
Object.preventExtensions(obj);
obj.url = 'https://israynotarray.com/'; console.log(obj);
|
當然這也是一個靜默錯誤:
1 2 3 4 5 6 7 8 9 10
| 'use strict' var obj = { myName: 'Ray', blog: {}, };
Object.preventExtensions(obj);
obj.url = 'https://israynotarray.com/'; console.log(obj);
|
否則正常來講你應該會看這樣子的回傳:
1 2 3 4 5
| { myName: "Ray", blog: {}, url: "https://israynotarray.com/" }
|
當然你可能會好奇「我怎麼知道這個物件能不能被擴充?」,其實可以使用 Object.isExtensible 來查看是否可以被擴充:
1 2 3 4 5 6 7 8 9 10 11
| var obj = { myName: 'Ray', blog: {}, };
Object.preventExtensions(obj); console.log('是否可以被擴充:', Object.isExtensible(obj));
obj.url = 'https://israynotarray.com/';
console.log(obj);
|
但是這邊要注意一件事情,本身該物件若調整成不可被擴充之後,原有的屬性依然是可以調整的:
1 2 3 4 5 6 7 8 9 10
| var obj = { myName: 'Ray', blog: {}, };
Object.preventExtensions(obj);
obj.blog = 'https://israynotarray.com/';
console.log(obj);
|
因此你可以理解成 Object.preventExtensions 是禁止物件去新增一個屬性,但可以調整現有的屬性或者是刪除屬性,因此 preventExtensions 並不會調整 defineProperty 底下的 writable 等,但接下來的另外兩個語法就稍微比較特別一點。
接下來的 Object.seal 就真的滿特別的,它包含物件無法被擴充之外也無法讓屬性被新增刪除,更不用說使用 defineProperty 重新調整屬性特徵,就如同它的翻譯一樣,整個物件都被封裝起來了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var obj = { myName: 'Ray', blog: {}, };
Object.seal(obj);
console.log('是否被封裝:', Object.isSealed(obj)); console.log('查看 myName 的屬性特徵:', Object.getOwnPropertyDescriptor(obj, 'myName'));
obj.myName = 'QQ123'; obj.url = 'https://israynotarray.com/'; delete obj.myName obj.blog = false;
Object.defineProperty(obj, 'myName', { configurable: true, });
|
因此 Object.seal 的重點在於「它包含物件無法被擴充之外也無法讓屬性被新增刪除,更不用說使用 defineProperty 重新調整屬性特徵」,但是它可以修改屬性,因此 Object.seal 與前面 preventExtensions 是有關連的,物件會先被限制擴充再加上 seal 避免物件被刪除。
最後一個是 Object.freeze,freeze 會針對當前物件給予 seal 再去限制不可調整值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var obj = { myName: 'Ray', blog: {}, };
Object.freeze(obj);
console.log('是否被封裝:', Object.isSealed(obj)); console.log('是否可以被擴充:', Object.isExtensible(obj)); console.log('是否被凍結:', Object.isFrozen(obj)); console.log('查看 myName 的屬性特徵:', Object.getOwnPropertyDescriptor(obj, 'myName'));
obj.myName = 'QQ123'; delete obj.myName; obj.url = 'https://israynotarray.com/';
console.log(obj);
|
就算你使用 Object.defineProperty 調整屬性特徵也一樣是會出現錯誤的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var obj = { myName: 'Ray', blog: {}, };
Object.freeze(obj);
console.log('是否被封裝:', Object.isSealed(obj)); console.log('是否可以被擴充:', Object.isExtensible(obj)); console.log('是否被凍結:', Object.isFrozen(obj)); console.log('查看 myName 的屬性特徵:', Object.getOwnPropertyDescriptor(obj, 'myName'));
obj.myName = 'QQ123'; delete obj.myName; obj.url = 'https://israynotarray.com/';
|
最後的最後,在上面其實沒有特別講到如果是物件下的物件,例如 obj.blog 的話那麼上面三個方法是否可以調整?其實都是可以調整的,因為物件本身是採用參考位置,因此不論任何一個方法都是針對當前物件設置封裝凍結等,而不會針對其他物件調整:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var obj = { myName: 'Ray', blog: {}, };
Object.freeze(obj);
console.log('是否被封裝:', Object.isSealed(obj)); console.log('是否可以被擴充:', Object.isExtensible(obj)); console.log('是否被凍結:', Object.isFrozen(obj)); console.log('查看 myName 的屬性特徵:', Object.getOwnPropertyDescriptor(obj, 'myName'));
obj.myName = 'QQ123'; delete obj.myName; obj.url = 'https://israynotarray.com/';
obj.blog.url = 'https://israynotarray.com/'; console.log(obj);
|
當然如果你希望深層物件也跟著凍結的話,其實 MDN 也有提供範例程式碼,以下擷取 MDN:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function deepFreeze(obj) {
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) { var prop = obj[name];
if (typeof prop == 'object' && prop !== null) deepFreeze(prop); });
return Object.freeze(obj); }
obj2 = { internal: {} };
deepFreeze(obj2); obj2.internal.a = 'anotherValue'; obj2.internal.a;
|
基本上你設置了上述任一個方法後就無法再次取消,最主要是如果開發中你可以任意取消的話,那麼就失去了這些語法的意義,畢竟這些語法最主要是避免不可被修改的物件被修改。
參考文獻
整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ
Advertisement