JavaScript Set與WeakSet

admin發表於2019-04-25

本文將通過程式碼例項詳細介紹一下Set與WeakSet資料結構的用法。

兩種資料結構是ES2015新增,與陣列非常相似,但是區別也很明顯,下面會介紹。

一.Set物件:

通過Set()建構函式可以建立一個Set物件例項。

此物件與陣列非常相似,但是Set物件中的元素不能重複,這是與陣列的最重大區別。

利用此特點,我們可以輕鬆實現之前之前需要繁瑣的程式碼實現的功能。

首先看一段程式碼例項:

[JavaScript] 純文字檢視 複製程式碼執行程式碼
let set = new Set();
[1, 2, 3, 4, 2, 8, 4].map(function (elem) {
  set.add(elem);
})
for (var elem of set) {
  console.log(elem)
}

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/25/112359krzvyz60zxve5ivh.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

對上述程式碼分析如下:

(1).首先通過Set()建構函式建立一個Set物件例項set。

(2).通過map方法迴圈遍歷陣列的中的每一個元素,嘗試將它們逐一新增到set物件中。

(3).最後通過for of迴圈遍歷set物件中的每一個成員,可以發現沒有重複的。

(4).也就是說,set中已經存在的成員,不允許再被新增。

特別強調一點,JavaScript中NaN與它自身不相等,但是在Set物件中也只能存在一個,不再通過程式碼演示。

建構函式Set的引數可以是陣列,陣列的成員可以作為Set物件的成員,具有去重功能。

程式碼例項如下:

[JavaScript] 純文字檢視 複製程式碼執行程式碼
let set = new Set([1, 2, 3, 4, 2, 8, 4]);
let arr = [...set];
console.log(arr);

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/25/112436pg2bnj2ulbqu1evw.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

上述程式碼分析如下:

(1).建構函式的引數是一個陣列,陣列元素有重複現象。

(2).由於Set物件不允許有重複元素,所以能夠做到去重功能。

(3).然後再通過展開運算子,將set物件成員寫入陣列中,從而簡單實現了陣列去重功能。

此物件具有遍歷器結構,關於遍歷介面可以參閱JavaScript Iterator 遍歷器一章節。

具有遍歷器介面,也就意味著可以使用展開運算子和for of迴圈,上面程式碼已經涉及,不再演示。

更多內容可以參閱如下兩篇文章:

(1).JavaScript for of迴圈一章節。

(2).JavaScript 展開運算子一章節。

[JavaScript] 純文字檢視 複製程式碼執行程式碼
let set = new Set();
set.add({});
set.add({});
console.log(set.size);

 程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/25/112534wfm997cfczzamr3f.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

上述程式碼分析如下:

(1).前文已經介紹,Set物件中不能有重複元素。

(2).但是上述物件貌似新增了兩個一模一樣的元素,並且新增成功。

(3).事實並非如此,兩個物件雖然外觀一模一樣,是兩個獨立的物件。

(4).物件是引用型別資料,是否相同,需要看它們的儲存地址是否相同。

關於應用型別資料可以參閱JavaScript 值型別和引用型別一章節。

物件屬性與方法:

此物件具有諸多屬性與方法,考慮到篇幅問題,本文不做介紹。

下面是屬性與方法相關文章列表:

(1).Set.prototype.add() 方法一章節。

(2).Set.prototype.clear() 方法一章節。

(3).Set.prototype.delete() 方法一章節。

(4).Set.prototype.entries() 方法一章節。

(5).Set.prototype.forEach() 方法一章節。

(6).Set.prototype.has() 方法一章節。

(7).Set.prototype.values() 方法一章節。

(8).Set.prototype.constructor 屬性一章節。

(9).Set.prototype.size 屬性一章節。

二.WeakSet物件:

從名稱看,此物件與Set物件非常相似,其成員也不能重複。

Set物件的成員可以是任意型別,但是WeakSet物件成員只能是引用型別。

程式碼例項如下:

[JavaScript] 純文字檢視 複製程式碼
let weakset = new WeakSet();
weakset.add(5)

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/25/112701knjnxnaco9al14z9.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

由於上述程式碼新增的成員非引用型別,所以會報錯。

[JavaScript] 純文字檢視 複製程式碼執行程式碼
let weakset = new WeakSet();
weakset.add({});
console.log(weakset.size);

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/25/112729kiltt23t22ytslzi.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

上述程式碼成功為WeakSet物件新增一個引用型別成員。

WeakSet建構函式的引數也可以是陣列,與Set建構函式相同。

但是,陣列的成員必須是引用型別,比如[1,2,3,4]不可以作為WeakSet()建構函式的引數。

但是[[1, 2], [3, 4]]可以作為建構函式的引數,程式碼例項如下:

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/25/112753c8eiebi9e8lrmrrb.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

前面介紹了WeakSet物件的成員必須是引用型別資料,此引用型別資料是弱引用。

也就是垃圾回收機制不會考慮WeakSet物件例項對該成員物件的引用,如果其他物件不再引用此物件成員。

那麼垃圾回收機制就會收回此物件成員所佔用的空間,並不會考慮此物件成員是否當前仍然是WeakSet的成員。

特別說明:與Set物件不同,WeakSet物件不具有size屬性,也不能遍歷。

看一段簡單的程式碼例項:

[JavaScript] 純文字檢視 複製程式碼
let weakset = new WeakSet()
class Antzone {
  constructor() {
    weakset.add(this)
  }
  method () {
    if (!weakset.has(this)) {
      throw new TypeError("方法只能在Antzone例項上呼叫")
    }
  }
}

上述程式碼分析如下:

(1).通過weakset物件儲存Antzone類物件例項。

(2).method是Antzone類的例項方法,在呼叫此方法的時候會通過weakset.has方法判斷是否具有this物件例項,如果沒有則說明不是通過Antzone物件例項呼叫,因為方法可以通過apply等改變呼叫物件。由於作為成員的Antzone物件例項是弱引用,所以刪除例項的時候,不用考慮weakset,也不會出現記憶體洩漏。

物件屬性與方法:

(1).WeakSet.prototype.add() 方法一章節。

(1).WeakSet.prototype.delete() 方法一章節。

(1).WeakSet.prototype.has() 方法一章節。

(1).WeakSet.prototype.constructor 屬性一章節。

相關文章