JavaScript Map

了解 ES6 JavaScript 當中的內建資料結構: Map

前言

JavaScript ES6 中有一個用法與物件近似的資料結構我一直不是很清楚用途。 —— Map,這篇文章會主要拿熟知常見的物件與 Map 來做比較以分辨出 Map 的特性與使用時機。

語法

// Init 建立
const map = new Map([
[1, 'apple'],
[2, 'orange'],
]);
// Set - 新增 key 與 value
map.set(3, 'banana');
// Get - 根據 key 取 value
map.get(3);
// Delete - 根據 key 刪除
map.delete(3);
// Clear - 清空所有資料
map.clear();
// Has - 檢查 key 是否存在
map.has(2);
// Keys - 獲取所有 key
map.keys();
// Values - 獲取所有 Value
map.values();
// Entries - 獲取 key-value 組合
map.entries();
// Length - 資料長度
map.size;
// Iteration for - for 遍歷
for (const [key, value] of map) {
console.log({ key, value });
}
// Iteration forEach - forEach 遍歷
map.forEach((value, key) => {
console.log({ key, value });
});
// Swap Keys & Values - 交換 key 與 value
const swapMap = Array.from(map).reduce((acc, [key, value]) => acc.set(value, key), new Map());

相較物件來說 Map 的優點

  1. 沒有歷史遺留問題 :除了特別儲存進去的鍵之外 Map 並不存在任何鍵,相較於 Object 來說更為明確簡潔。
const myMap = {};
myMap.valueOf; // => [Function: valueOf]
myMap.toString; // => [Function: toString]
myMap.hasOwnProperty; // => [Function: hasOwnProperty]
myMap.isPrototypeOf; // => [Function: isPrototypeOf]
myMap.propertyIsEnumerable; // => [Function: propertyIsEnumerable]
myMap.toLocaleString; // => [Function: toLocaleString]
myMap.constructor; // => [Function: Object]

並且在取值時,舉例來說 books[id] 取值可能會需要擔心 id 鍵是否真的存在於 books 當中,因此需要在取值前先判斷是否存在,像是:

// 一般檢測物件是否具備某個鍵的方法
if (books.hasOwnProperty(id)) {
}
// 應對某些特殊情況
if (Object.prototype.hasOwnProperty.call(books, id)) {
}

但在 Map 可以直接使用對應的方法去查詢,並且可以確保預設沒有額外的鍵存在,看看以下簡潔的語法!

myMap.get(key);
for (const [key, value] of myMap) {
}
  1. 允許任何類型的鍵Map 的鍵可以是任何類型,包括物件、函式以及原始類型(字串、數字等)。相較之下,物件的鍵只能是字串或符號(Symbol🔗)。

  2. 明確的順序 :Map 保留鍵值對的插入順序,這使得迭代時能夠按照插入順序進行,而物件則無法保證。這樣明確順序的特性也讓 Map 的遍歷效率更快更好。詳細可以看看 builder.io 的線上測驗🔗

  3. 安全性 :設置用戶提供的 鍵/值 配對到物件上可能讓人惡意向程式中注入惡意構造的物件(Object Injection Attacts🔗),從而改變程式的邏輯或執行未預期的操作,而使用 Map 的方法則可以安全的避免這種情況。

相較物件來說 Map 的缺點

  1. 學習曲線:雖然 Map 使用方法簡單,但出現頻率相對較少,因此不了解的人可能需要額外的學習成本。
  2. 記憶體開銷:由於 Map 的實現細節,儲存同樣數量的鍵值對時,Map 通常比物件佔用更多的記憶體。
  3. JSON 支援:物件能夠直接被轉換為 JSON 格式,Map 則需要額外的處理才能序列化為 JSON 格式。
// Object 轉換為 JSON
const obj = { key: 'value' };
const jsonString = JSON.stringify(obj);
// Map 轉換為 JSON
const map = new Map();
map.set('key', 'value');
// 需要轉換為物件或陣列後才能轉換為 JSON
const mapToObject = Object.fromEntries(map);
const mapToJsonString = JSON.stringify(mapToObject);

使用時機

總結來說可以把 Map 當作是用來頻繁讀寫的物件,它具備更好的性能、更明確的語法,而物件可以用作儲存固定的鍵值對,在不需要頻繁讀寫的情況下使用。

延伸閱讀