深入理解JavaScript中的WeakMap和WeakSet

發表於2023-09-18

公眾號

image.png

小冊

這是我整理的學習資料,非常系統和完善,歡迎一起學習

深入理解JavaScript中的WeakMap和WeakSet

在JavaScript的ES6版本中,引入了兩種新的資料結構——WeakMapWeakSet。與Map和Set相比,這兩種資料結構有一些特殊的特點和用途,因此在某些場合下,它們是更好的選擇。本文將深入探討WeakMapWeakSet的特性和用途。

1. WeakMap和WeakSet概述

在我們深入研究這兩種新的資料結構之前,首先來了解一下它們的基本特性。

1.1 WeakMap

WeakMap是一種鍵值對的集合,類似於Map。不過,WeakMapMap有幾個重要的區別:

  • WeakMap中,只有物件可以作為鍵。換句話說,我們不能使用基本型別(如數字,字串,布林值等)作為WeakMap的鍵。
  • WeakMap的鍵是弱引用的。這意味著,如果一個物件只被WeakMap引用,那麼這個物件可以被垃圾回收(GC)。當這個物件被垃圾回收後,它對應的鍵值對也會從WeakMap中自動移除。
  • WeakMap不可遍歷,也就是說,我們不能使用像for...of這樣的迴圈來遍歷WeakMap

由於這些特性,WeakMap在處理記憶體洩漏問題和管理物件私有資料等場景中有著顯著的優勢。

1.2 WeakSet

WeakSet也是一種集合,類似於SetWeakSetSet的主要區別包括:

  • WeakSet中,只有物件可以作為值。也就是說,我們不能將基本型別(如數字,字串,布林值等)新增到WeakSet中。
  • WeakSet中的物件是弱引用的。如果一個物件只被WeakSet引用,那麼這個物件可以被垃圾回收。當這個物件被垃圾回收後,它會自動從WeakSet中移除。
  • WeakSet不可遍歷,也就是說,我們不能使用像for...of這樣的迴圈來遍歷WeakSet

WeakSet在處理物件的唯一性、記憶體洩漏等問題上有其獨特的應用。

2. WeakMap深入解析

2.1 WeakMap的建立和使用

我們可以使用new WeakMap()來建立一個新的WeakMap。在建立了WeakMap之後,我們可以使用set方法來新增新的鍵值對,

使用get方法來獲取某個鍵對應的值,使用delete方法來移除某個鍵及其對應的值,使用has方法來檢查WeakMap中是否存在某個鍵。

let weakMap = new WeakMap();

let obj1 = {};
let obj2 = {};

// 新增鍵值對
weakMap.set(obj1, 'Hello');
weakMap.set(obj2, 'World');

// 獲取值
console.log(weakMap.get(obj1)); // 輸出: 'Hello'
console.log(weakMap.get(obj2)); // 輸出: 'World'

// 檢查鍵是否存在
console.log(weakMap.has(obj1)); // 輸出: true
console.log(weakMap.has(obj2)); // 輸出: true

// 刪除鍵值對
weakMap.delete(obj1);
console.log(weakMap.has(obj1)); // 輸出: false

2.2 WeakMap和記憶體管理

WeakMap最重要的特性就是其鍵對物件的弱引用。這意味著,如果一個物件只被WeakMap引用,那麼這個物件可以被垃圾回收。這樣就可以防止因為長時間持有物件引用導致的記憶體洩漏。

例如,如果我們在Map中儲存了一些物件的引用,即使這些物件在其他地方都已經不再使用,但是由於它們仍被Map引用,所以它們不能被垃圾回收,這就可能導致記憶體洩漏。然而,如果我們使用WeakMap來儲存這些物件的引用,那麼當這些物件在其他地方都不再使用時,它們就會被垃圾回收,從而防止了記憶體洩漏。

2.3 WeakMap和物件私有資料

WeakMap還常常被用來儲存物件的私有資料。這是因為WeakMap的鍵不可遍歷,所以我們可以利用這個特性來儲存一些只有特定程式碼能夠訪問的資料。

例如,我們可以建立一個WeakMap,然後使用這個WeakMap來儲存每個物件的私有資料,像這樣:

let privateData = new WeakMap();

function MyClass() {
  privateData.set(this, {
    secret: 'my secret data',
  });
}

MyClass.prototype.getSecret = function() {
  return privateData.get(this).secret;
};

let obj = new MyClass();
console.log(obj.getSecret()); // 輸出: 'my secret data'

在這個例子中,我們建立了一個MyClass的類,每一個MyClass的例項都有一個私有資料secret。我們使用WeakMap來儲存這個私有資料。這樣,我們就可以在MyClass的方法中訪問這個私有資料,但是其他的程式碼無法訪問它。

3. WeakSet深入解析

3.1 WeakSet的建立和使用

我們可以使用new WeakSet()來建立一個新的WeakSet。在建立了WeakSet之後,我們可以使用add方法來新增新的物件,使用delete方法來移除某個物件,使用has方法來檢查WeakSet中是否存在某個物件。

let weakSet = new WeakSet();

let obj1 = {};
let obj2 = {};

// 新增物件
weakSet.add(obj1);
weakSet.add(obj2);

// 檢查物件是否存在
console.log(weakSet.has(obj1)); // 輸出: true
console.log(weakSet.has(obj2)); // 輸出: true

// 刪除物件
weakSet.delete(obj1);
console.log(weakSet.has(obj1)); // 輸出: false

3.2 WeakSet和物件唯一性

WeakSet可以用來檢查一個物件是否已經存在。由於WeakSet中的每個物件都是唯一的,所以我們可以利用這個特性來確保我們不會新增重複的物件。

例如,我們可以建立一個WeakSet,然後使用這個WeakSet來儲存所有我們已經處理過的物件,像這樣:

let processedObjects = new WeakSet();

function processObject(obj) {
  if (!processedObjects.has(obj)) {
    // 處理物件
    // ...

    // 將物件新增到WeakSet中,表示我們已經處理過這個物件
    processedObjects.add(obj);
  }
}

在這個例子中,我們在每次處理一個物件之前,都會檢查這個物件是否已經被處理過。如果這個物件已經被處理過,我們就不會再處理它。這樣,我們就可以確保我們不會重複處理同一個物件。

3.3 WeakSet和記憶體管理

WeakMap一樣,WeakSet中的物件也是弱引用的,所以WeakSet也有優秀的記憶體管理特性。如果一個物件只被WeakSet引用,那麼這個物件可以被垃圾回收。這樣就可以防止因為長時間持有物件引用導致的記憶體洩漏。

例如,如果我們在Set中儲存了一些物件的引用,即使這些物件在其他地方都已經不再使用,但是由於它們仍被Set引用,所以它們不能被垃圾回收,這就可能導致記憶體洩漏。然而,如果我們使用WeakSet來儲存這些物件的引用,那麼當這些物件在其他地方都不再使用時,它們就會被垃圾回收,從而防止了記憶體洩漏。

4. 結論

在JavaScript的ES6版本中,引入了WeakMapWeakSet這兩種新的資料結構。與MapSet相比,它們有一些特殊的特點和用途,使它們在處理記憶體洩漏問題、管理物件私有資料、處理物件的唯一性等場景中有顯著的優勢。理解它們的特性和用法,可以幫助我們更有效地使用JavaScript來編寫高效、穩定的程式碼。

相關文章