Map in ES6 JavaScript
了解 ES6 JavaScript 當中的內建資料結構: Map
前言
JavaScript ES6 中有一個用法與物件近似的資料結構我一直不是很清楚用途。 —— Map
,這篇文章會主要拿熟知常見的物件與 Map
來做比較以分辨出 Map
的特性與使用時機。
語法
// Init 建立const map = new Map([ [1, 'apple'], [2, 'orange'],]);
// Set - 新增 key 與 valuemap.set(3, 'banana');// Get - 根據 key 取 valuemap.get(3);// Delete - 根據 key 刪除map.delete(3);// Clear - 清空所有資料map.clear();// Has - 檢查 key 是否存在map.has(2);// Keys - 獲取所有 keymap.keys();// Values - 獲取所有 Valuemap.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 與 valueconst swapMap = Array.from(map).reduce((acc, [key, value]) => acc.set(value, key), new Map());
相較物件來說 Map 的優點
- 沒有歷史遺留問題 :除了特別儲存進去的鍵之外
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) {}
-
允許任何類型的鍵 :
Map
的鍵可以是任何類型,包括物件、函式以及原始類型(字串、數字等)。相較之下,物件的鍵只能是字串或符號(Symbol)。 -
明確的順序 :Map 保留鍵值對的插入順序,這使得迭代時能夠按照插入順序進行,而物件則無法保證。這樣明確順序的特性也讓
Map
的遍歷效率更快更好。詳細可以看看 builder.io 的線上測驗。 -
安全性 :設置用戶提供的
鍵/值
配對到物件上可能讓人惡意向程式中注入惡意構造的物件(Object Injection Attacts),從而改變程式的邏輯或執行未預期的操作,而使用Map
的方法則可以安全的避免這種情況。
相較物件來說 Map 的缺點
- 學習曲線:雖然
Map
使用方法簡單,但出現頻率相對較少,因此不了解的人可能需要額外的學習成本。 - 記憶體開銷:由於 Map 的實現細節,儲存同樣數量的鍵值對時,Map 通常比物件佔用更多的記憶體。
- JSON 支援:物件能夠直接被轉換為 JSON 格式,Map 則需要額外的處理才能序列化為 JSON 格式。
// Object 轉換為 JSONconst obj = { key: 'value' };const jsonString = JSON.stringify(obj);
// Map 轉換為 JSONconst map = new Map();map.set('key', 'value');
// 需要轉換為物件或陣列後才能轉換為 JSONconst mapToObject = Object.fromEntries(map);const mapToJsonString = JSON.stringify(mapToObject);
使用時機
總結來說可以把 Map
當作是用來頻繁讀寫的物件,它具備更好的性能、更明確的語法,而物件可以用作儲存固定的鍵值對,在不需要頻繁讀寫的情況下使用。
延伸閱讀
- Map - mdn
- Use Maps more and Objects less - builder.io
- You Should Use Maps and Sets in JS - Syntax
- Map vs Object in JavaScript - Leigh Halliday