ES6 推出了 Map 物件,讓開發者可以透過這個特製資料結構進行鍵值對(key-value pairs)的操作。然而 JavaScript 原始物件(plain object)就可以用來做鍵值對的操作,為什麼還需要 Map 物件呢? Map 物件解決了什麼問題?那與 Set 又有什麼不同呢?本篇來詳細說明一下。
Map 與 object 的差別
原始物件的鍵(key)只可以是字串,但 Map 的鍵可以是任何東西
在用鍵值對處理資料時,很常開發者會用各類東西做為鍵(key),但是在 JavaScript,如果用原始物件,不管用什麼當做鍵,都會被轉換成字串,這往往會造成一切錯誤,例如被轉成字串時,原本兩格不同的鍵被轉成同樣的字串,這就導致撞鍵問題。而 Map 物件解決了這個問題,開發者想用什麼當鍵都可以。
原始物件不支援迭代(iteration),但 Map 物件有
過去我們沒辦法直接對一個原始物件用 for...of
、forEach
來迭代,而是需要用額外的方法,例如 Object.entries
、Object.keys
來協助。但是 Map 物件是可迭代的(iterable),所以我們可以直接對 Map 物件用 for...of
、forEach
。
原始物件的元素沒有順序性,Map 物件則有順序
上面提到 Map 物件是可迭代的,它有一個相關的優點是在迭代時是有順序性的。過去用原始物件,即使用用物件方法 (例如 Object.entries
、Object.keys
來協助迭代),迭代出的結果順序不一定會是我們放入鍵值對的順序。
但有時候在寫演算法時,我們需要保留順序,這時 Map 物件就會好用很多。
Map 提供許多鍵值對常用的方法,但原始物件沒有
舉例來說,如果要知道一個鍵值對的大小,Map 有 size
方法,簡單又好用;但如果用原始物件,我們可能需要使用 Object.keys
然後搭配 .length
去自己找該物件有多少個鍵,這就麻煩很多。
另外 Map 也有提供 clear
方法,可以一次把所有鍵值對刪掉;如果是原始物件就需要一個一個刪。
Map
類似於 Object 的資料結構,都是用鍵與值(key-value pair)的形式儲存資料格式,但還是有許多差異。
Map 本身是一種構造函式,用來生成 Map
這種數據結構,具體做法是 new Map()
來生成實例。
常見操作方法
Map 常見操作方法有
set(key, value)
: Map 新增元素。get(key)
: 透過鍵(Key)查詢特定元素,並返回。has(key)
: 判斷 Map 中是否存在某鍵(key)。delete(key)
: 透過鍵(Key)從 Map 中移除特定元素。size
: 方法得到元素的數量。
常見遍歷方法
Map 常見遍歷方法(遍歷順序會是插入順序)
values()
: 返回 Map 中所有元素的值。keys()
: 方法等同於values()
。entries()
: 返回 Set 中所有的元素,因在 Set 中沒有鍵值(Key),返回的元素會是[value, value]
的形式。
Map 和 WeakMap 的區別
WeakMap 的方法和使用部分與 Map 資料結構相近,本區塊會專注在這兩者不同之處。
- WeakMap 只允許物件(Object) 作為鍵名(key),但是 null 除外。而 Map 則是可接受各種資料類型作為鍵名(key)。
- WeakMap 中的鍵名是**「弱引用」(weak reference)**,鍵名(key)所指向的對象可以被垃圾回收,此時的鍵名(key)是無效的。
|
|
在上方的強引用程式碼中,雖然 fruit
物件最後被重新賦值為 null
(意思等同於無法再透過 fruit
變數獲取該對象值,因為其中的引用被斷開),但由於 food
與此物件間存在強引用,所以被保留在記憶體中,這就是前面提到的,強引用會防止物件被垃圾回收,並將物件保留在記憶體當中; 弱引用則相反,並不能防止物件被垃圾回收,當 JavaScript 執行環境執行垃圾回收時,上述弱引用例子中的 fruit
物件會被從記憶體和 WeakMap 中刪除。
弱引用的適用情境在於,如果引用的物件在未來可能會被刪除的情況、且不想防止被垃圾回收時,就適合用 WeakMap 或 WeakSet。例如,如果我們想要記錄一些與 DOM 節點相關的數據,有一種方法是使用 Expando 擴充節點上的資訊,但壞處是會直接修改到這個 DOM 節點、且如果未來這個節點被移除時,相關資訊不會被垃圾回收掉,這時如果是使用 WeakMap 就會是很好的替代方案。
備註: 如果直接將弱引用程式碼的例子在 JavaScript 執行環境中執行,可能還是會看到 WeakMap 中有值,這是因為 JavaScript 執行環境會在特定的時間點執行垃圾回收。
Set
Set 這個數據結構類似陣列,但是裡面的元素值都是唯一,不會有重複的值,無論此值是原始型別(primitive values)或引用型別(object references)。在 JavaScript 當中,Set 本身是一種構造函式,用來生成 Set 這種數據結構,具體的做法是透過 new Set()
來生成實例。
常見操作方法
Set 常見操作方法有
add(value)
: 用來新增值。delete(value)
:用來刪除每個 Set 中的值。has(value)
:判斷 Set 中是否存在每個值。size
: 方法得到元素的數量,與陣列使用arr.length
方法不一樣。
Set 中沒有鍵值(Key),如果使用 entries()
遍歷,返回的元素會是 [value, value]
的形式。
|
|
Set 和 WeakSet 的區別
WeakSet 的方法和使用部分與 Set 資料結構相近,本區塊會專注在這兩者不同之處。
- WeakSet 內的元素值只允許是物件(Object),但 Set 可接受各種資料類型的值。
|
|
- WeakSet 內的元素都是**「弱引用」(weak reference)**,可以被垃圾回收機制回收。假如使用 Set ,即使某個被存入的值,在其他地方已經沒有被引用,該值仍會存在於 Set 當中,不會被垃圾回收。但如果是 WeakSet,則會被垃圾回收。如果要更有意識地做記憶體管理,WeakSet 在許多時候能派上用場。
|
|