首頁 > 軟體

JavaScript 物件不變性介紹

2022-02-09 10:00:32

1. 基本概念

物件不變性在任何程式語言中都是一個重要的概念。它會限制物件修改並防止不需要的更改。簡而言之,物件的不變性就是將它的狀態變為唯讀的,下面就來看看在 JavaScript 中的物件不變性。​

JavaScript中,一個物件可以有一個或多個屬性。每個屬性都是一個鍵值對,

下面是一個物件:

const user = {
    name: 'CUGGZ',
  age: 24,
}

這裡使用const關鍵字定義了一個物件,它具有兩個屬性。預設情況下,物件是可變的,也就是說,我們可以給物件新增新屬性,修改現有屬性或者刪除已有屬性。而在某些情況下,我們可能希望物件是不可變的,即不能新增新屬性,也不能修改和刪除已有屬性。

2. Object.freeze()

顧名思義,freeze() 就是用來凍結物件的。只需要將不想被更改的物件傳遞給它,就會返回該物件的不可變版本:

const user = {
    name: 'CUGGZ',
  age: 24,
}

const freezeUser = Object.freeze(user);
freezeUser.age = 18;
console.log(freezeUser.age) // 24

這時,新的物件就不可變了,相當於被凍結了。​

JavaScript了,提供了一個Object.isFrozen(),它可以用來判斷一個物件是否被凍結:

Object.isFrozen(user)  // false
Object.isFrozen(freezeUser)  // true

需要注意的是, Object.freeze() 方法不會影響巢狀物件,對於巢狀物件,凍結後仍然是可以操作的:

const user = {
    name: 'CUGGZ',
  age: 24,
  article: {
      title: 'learn javascript',
    number: 1234
  }
}

const freezeUser = Object.freeze(user);
freezeUser.age = 18
freezeUser.article.number = 666;
console.log(freezeUser.age)             // 24
console.log(freezeUser.article.number); // 666

可以看到,使用Object.freeze() 方法凍結的物件,age是不可以更改的,但是巢狀物件的number屬性還是可以修改的。如果需要凍結巢狀物件使其不可變,就需要使用循遞回來逐級凍結,下面是一個簡單的遞迴凍結實現:

const deepFreeze = obj => {
  Object.keys(obj).forEach(prop => {
    if (typeof obj[prop] === 'object') {
        deepFreeze(obj[prop]);
    }
  });
  return Object.freeze(obj);
};

deepFreeze(user);

Object.freeze()方法除了可以用來凍結物件以外,還可以用於凍結陣列,使其不可變:

const number = [1, 2, 3, 4, 5];
const freezeNumber = Object.freeze(number);
freezeNumber.push(6);

此時就會報錯了:

VM210:3 Uncaught TypeError: Cannot add property 5, object is not extensible

3. Object.seal()

Object.seal() 顧名思義就是密封物件,它是另一種使物件不可變的方法。相對於freeze(),Object.seal() 方法僅保護物件不能新增和刪除屬性,它允許更新現有的屬性。

const user = {
    name: 'CUGGZ',
  age: 24,
}

const sealUser = Object.seal(user);
sealUser.age = 18;
delete sealUser.name;
console.log(sealUser)   // {name: 'CUGGZ', age: 18}

可以看到,我們識圖刪除物件中的name屬性,刪除失敗;而修改已有的age屬性,修改成功。​

Object.seal()方法和Object.freeze()一樣,對於巢狀的物件,是無法實現不可變的,如果想要實現,同樣要使用遞回來一層層來密封物件。​

JavaScript同樣提供了一個Object.isSealed() 方法來確認物件的密封狀態:

Object.isSealed(user)      // false
Object.isSealed(sealUser)  // true

4. const關鍵字?

你是否會認為,使用const關鍵字也能達到相同的結果呢?實際上,他們是不一樣的,當我們使用const關鍵字來建立物件時,它會阻止我們重新分配值,但是我們可以更新、新增、刪除已有物件的屬性:

const user = {
    name: 'CUGGZ',
  age: 24,
}

delete user.age;
user.height = 180;
user.name = 'hello';
console.log(user);  // {name: 'hello', height: 180}

而如果我們給user重新賦值,那麼就會報錯了:

Uncaught TypeError: Assignment to constant variable.

因此,const關鍵字僅僅是提供了賦值的不變性,而不會提供值的不變性(對於物件來說)。

5. 總結

本文簡單介紹了兩種可以用於使JavaScript不可變的方法。簡而言之,Object.seal()方法會阻止更新、刪除和向物件新增新屬性,Object.seal()只會阻止新增和刪除屬性。、

除此之外,JavaScript還提供了一個Object.preventExtensions()方法,該方法可以讓一個物件變的不可延伸,也就是永遠不能再新增新的屬性。

const user = {
    name: 'CUGGZ',
  age: 24,
}

const newUser = Object.preventExtensions(user);
newUser.height = 180;
console.log(newUser);  //  {name: 'CUGGZ', age: 24}

最後來看看它們三個的對比:

方法/操作讀取建立更新刪除
Object.freeze()✔️
Object.seal()✔️✔️
Object.preventExtensions()✔️✔️✔️

到此這篇關於JavaScript 物件不變性介紹的文章就介紹到這了,更多相關JavaScript 物件不變性內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com