Day22 | 物件方法介紹 - 針對物件本身操作的方法

❒ 前言

上篇提到的 Object.definePropertyObject.defineProperties 為調整物件本身的的屬性特徵方法,這篇將説說 preventExtensions ( 防止擴充 )seal ( 封裝 )Freeze ( 凍結 ) 這三種針對物件本身做限制的方法。

❒ 對物件本身調整的方法

  • preventExtensions ( 防止擴充 )
  • seal ( 封裝 )
  • Freeze ( 凍結 )

preventExtensionsealfreeze 的共同點與差異

  • 這三種方式都只能對物件本身做限制,無法對物件本身的巢狀屬性做限制。因為這三種方式只有淺層拷貝,物件本身的巢狀屬性就還是具有傳參考特性。
  • preventExtension 可以調整屬性特徵,sealfreeze 不能調整屬性特徵。
    • 備註:seal 屬性特徵中 writable 經測試是可以被調整的,似乎是 seal 有調整過導致,須留意。
  • 限制程度:freeze > seal > preventExtension

❒ preventExtension 防止擴充

特性: 不能「 新增 」物件本身屬性。

結構:Object.preventExtensions(物件)

Others :

  • Object.preventExtensions(物件) 可搭配 Object.isExtensible(物件) 做驗證看使用了 preventExtensions 後的物件是否可被擴充。

  • Object.preventExtensions(物件) 可搭 Object.getOwnPropertyDescriptor(物件, '屬性') 查看物件裡的特定屬性特徵。

  • 物件使用 preventExtensions 後的屬性特徵 ( 可使用 Object.getOwnPropertyDescriptor(物件, '物件屬性'); 查詢 )

    1
    2
    3
    4
    configurable: true,
    enumerable: true,
    value: 屬性本身的值
    writable: true

範例 1. 使用 preventExtensions 物件屬性特徵不會被改變

1
2
3
4
5
6
7
8
var person = {
a: 1,
b: 2,
c: {}
}
Object.preventExtensions(person);
console.log(`是否可被擴充 ${Object.isExtensible(person)}`);
console.log(Object.getOwnPropertyDescriptor(person, 'a'));
  • Object.isExtensible(person) 印出 false,可見 preventExtensions 的物件是不可被擴充的。
  • 以物件 a 屬性為例,Object.getOwnPropertyDescriptor(person, 'a') 可見 a 屬性的特徵們一樣為預設。
    截圖

範例 2. 只能針對物件本身做限制,無法對巢狀屬性做限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var person = {
a: 1,
b: 2,
c: {}
}

Object.preventExtensions(person);
// 調整屬性
person.a = 'a';
// 新增屬性
person.d = 'd';
// 調整巢狀屬性(新增)
person.c.inC = 'C is Here';

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

console.log(person);
  • 調整物件原有屬性:把物件內的 a 屬性的屬性值調整為 a 是沒問題的。
  • 於物件新增屬性:物件新增 d 屬性後,並沒有在物件中看見新增的 d 屬性,因為 preventExtensions 的用意就是無法新增屬性。
  • 調整物件內的巢狀屬性:preventExtensions 只能對物件本身做限制,無法對物件本身的巢狀屬性做限制,所以在巢狀屬性 c 內再新增 inC 屬性是沒問題的。
  • 調整物件屬性特徵:調整 a 的屬性特徵為 configurable: false,是沒問題的,可見 preventExtensions 是可以再次調整屬性特徵。

❒ seal 封裝

seal 特性

  • 物件屬性「無法新增刪除」,也無法重新配置特徵,但是可以調整目前屬性值。

    • 例外:seal 屬性特徵中 writable 經測試是可以被調整的,似乎是 seal 有調整過導致,須留意。
  • sealpreventExtensions 是有關聯性的,所以 seal 預設狀態為物件會被加上 preventExtensions,再把 「無法新增刪除,也無法重新配置特徵 」的特性加上。

  • 物件使用 seal 後的屬性特徵為 ( 可使用 Object.getOwnPropertyDescriptor(物件, '物件屬性'); 查詢 )

    1
    2
    3
    4
    configurable: false,
    enumerable: true,
    value: 屬性本身的值
    writable: true

seal 結構

Object.seal(物件);

範例 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var person = {
a: 1,
b: 2,
c: {}
}

Object.seal(person);
console.log(`是否可被封裝 ${Object.isSealed(person)}`);
// 調整屬性
person.a = 'a';
// 新增屬性
person.d = 'd';
// 調整巢狀屬性(新增)
person.c.inC = 'C is Here'

console.log(person);
  • 調整物件原有屬性:把物件內的 a 屬性的屬性值調整為 a 是沒問題的。
  • 於物件新增屬性:物件新增 d 屬性後,並沒有在物件中看見新增的 d 屬性,因為 preventExtensions 的用意就是無法新增屬性。
  • 調整物件內的巢狀屬性:preventExtensions 只能對物件本身做限制,無法對物件本身的巢狀屬性做限制,所以在巢狀屬性 c 內再新增 inC 屬性是沒問題的。
    截圖

範例 2. seal 封裝後是不能調整屬性特徵,但 writable 特徵例外

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var person = {
a: 1,
b: 2,
c: {}
}

Object.seal(person);
console.log(`是否可被封裝 ${Object.isSealed(person)}`);
console.log(Object.getOwnPropertyDescriptor(person, 'a'));

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

// Object.defineProperty(person, 'a' , {
// configurable: true,
// })
console.log(Object.getOwnPropertyDescriptor(person, 'a'));
console.log(person);

writable 特徵例外:

  • 修改了物件 a 的屬性特徵 writabletruefalse,並使用 Object.getOwnPropertyDescriptor 查看封裝後物件的屬性特徵,開發者工具依舊顯示 writable: false,驗證「 seal 封裝後的物件是無法調整屬性特徵 」
    未調整 a 屬性的屬性特徵狀態 ↑

    調整 a 屬性的writable屬性特徵狀態,被覆蓋為false

調整其他特徵:

  • 試圖改變其他特徵,會顯示錯誤訊息 Uncaught TypeError
    錯誤訊息 Uncaught TypeError

❒ freeze 凍結

特性

  • 物件使用 freeze 會讓此物件被凍結並且,

    • 無法新增刪除屬性。
    • 無法被修改屬性值。
    • 無法調整屬性特徵。
  • 物件使用 freeze 的屬性特徵 ( 可使用 Object.getOwnPropertyDescriptor(物件, '物件屬性'); 查詢 )

    1
    2
    3
    4
    configurable: false,
    enumerable: true,
    value: 屬性本身的值
    writable: false
  • freezesealpreventExtensions 是有關聯性的,所以 freeze 預設狀態為物件會被加上 preventExtensions ( 無法被擴展 ) 和 seal ( 封裝 ) 的特性,也無法被調整值。

結構

Object.freeze(物件);

範例 1. freeze 具備無法被擴展、封裝、凍結特性

1
2
3
4
5
6
7
8
9
10
var person = {
a: 1,
b: 2,
c: {}
}

Object.freeze(person);
console.log(`是否可被擴展 ${Object.isExtensible(person)}`); // false
console.log(`是否可被封裝 ${Object.isSealed(person)}`); // true
console.log(`是否可被凍結 ${Object.isFrozen(person)}`); // true

範例 2. 物件 freeze 後嘗試調整、新增屬性與調整巢狀屬性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var person = {
a: 1,
b: 2,
c: {}
}

Object.freeze(person);
// 調整屬性
person.a = 'a';
// 新增屬性
person.d = 'd';
// 調整巢狀屬性(新增)
person.c.inC = 'C is Here'
console.log(person);
  • 調整屬性與新增屬性:失敗。
  • 調整巢狀屬性:成功。
    • 因為物件傳參考特性外加淺層拷貝,所以 freeze 只能針對當下物件凍結,無法對物件屬性的巢狀屬性做凍結。
      ![截圖](https://ithelp.ithome.com.tw/upload/images/20221007/20119743GpfqLIxNAt.png)
      

範例 3. 物件凍結後嘗試調整屬性特徵

1
2
3
4
5
6
7
8
9
10
11
12
var person = {
a: 1,
b: 2,
c: {}
}

Object.freeze(person);

// 調整 writable 特徵
Object.defineProperty(person, 'a' , {
writable: true,
})
  • 此範例以調整 writable 特徵為例,會出現錯誤訊息 Uncaught TypeError
  • 另外嘗試調整其他屬性特徵 ( configurableenumerablevalue ),一樣會跳出錯誤訊息。

參考資訊

  • JavaScript 核心篇