我平常儲存資料還是習慣用 Object (物件),但最近發現了 MapWeakMap 這兩個資料結構,相比 Object,他們更靈活也更好用。文章也會跟大家介紹 MapWeakMap 這兩種資料結構,並深入介紹他們與 Object 的差異,歡迎一起加入使用(招手

什麼是 Map

Map 是 ES6 推出的資料結構,跟 Object 一樣也是 key-value (鍵值對) 的集合,但卻更為靈活,Map 的特點如下:

  • key 可以是任意型別:Object 的 key 只能是 stringSymbol,即使我們給 Object 如數字的 key 155,也是會強制轉成 string '155'。但 Map 的 key 可以是任意型別,不管是物件、函式、數字、布林 .... 等等,都能支援!
  • 有序性:它會記住你放入的順序,在遍歷時不會亂掉。
  • 可迭代 (iterable):不像 Object 需要用 Object.entries, Object.keys 等方法才能處理物件,而 Map 的預設迭代器是 entries(),所以可以直接使用迴圈 (for...of, forEach) 來遍歷它們的元素。
  • 可查詢數量:Map 可以使用 .size 就能取得裡面的數量,不像 Object 還需要自己去計算。

Map 的 key 可以是任意型別,這個特性真的很吸引人

const myMap = new Map();
const keyObj = { id: 1 };
myMap.set(keyObj, "這是物件的 key");
myMap.set("name", "Muki");
myMap.set(42, "這是數字");

console.log(myMap.get(keyObj)); // "這是物件的 key"
console.log(myMap.size); // 3

怎麼使用 Map?Map 的方法有哪些?

我將 Map 的所有方法整理成如下表格,包含方法的名稱、描述以及簡單的程式碼

方法名稱描述範例
set(key, value)設定 key-value,若 key 已存在會自動更新 valuemap.set('a', 1); // Map { 'a' => 1 }
get(key)取得指定 key 的 value,若 key 不存在則返回 undefinedmap.get('a'); // 1
has(key)檢查指定的 key 是否存在,返回 Booleanmap.has('a'); // true
delete(key)刪除指定的 key 及其 value,會返回 Boolean 表示是否刪除成功map.delete('a'); // true
clear()移除 Map 中的所有 key-valuemap.clear(); // Map {}
keys()返回一個包含所有 key 的迭代器,會按照插入順序返回map.keys(); // Iterator [ 'a', 'b' ]
values()返回一個包含所有 value 的迭代器,會按照插入順序返回map.values(); // Iterator [ 1, 2 ]
entries()返回一個包含所有 key-value 的迭代器,會按照插入順序返回map.entries(); // Iterator [ ['a', 1], ['b', 2] ]
forEach(callback)對每個 key 執行 callback functionmap.forEach((v, k) => console.log(k, v));

關於 Map 是否可迭代

▼ 如果我們直接用 for...of 迴圈遍歷 map 是可以做到的

for (let [key, value] of map) {
  console.log(`${key}: ${value}`);
}
// 輸出:
// a: 1
// b: 2

Map 物件本身是不可迭代的,只是特別的是,Map 有一個預設的迭代器,就是 entries(),所以當我們使用 for...of 迴圈遍歷 map 時,他會自動使用 map.entries() 做隱性的轉換。

▼ 因此,直接 for...of map 是合法的,等同於 for...of map.entries();但如果我們只想要 key 或 value,就要明確地使用 map.keys()map.values()

// 使用 map.keys()
for (let key of map.keys()) {
  console.log(key);
}

// 使用 map.values()
for (let value of map.values()) {
  console.log(value);
}

// 使用 map 等同於 map.entries()
for (let [key, value] of map) {
...
}

for (let [key, value] of map.entries()) {
  console.log(`${key}: ${value}`);
}

AD

範例:使用 Map 儲存表單欄位狀態的應用

我們用一個簡單的表單處理,來展示 Map 的好用之處。

▼ 假設現在有一個表單,包含姓名、電子郵件、年齡三個欄位,我們要用 Map 儲存這些欄位的值,並在遍歷時進行處理

// 建立一個 Map 來儲存表單欄位狀態
const formState = new Map();

// 填入表單資料
formState.set('name', 'MUKI');
formState.set('email', '[email protected]');
formState.set('age', 25);

// 遍歷表單狀態,檢查或顯示資料
for (let [field, value] of formState) {
  console.log(`欄位: ${field}, 值: ${value}`);
}

// 輸出:
// 欄位: name, 值: MUKI
// 欄位: email, 值: [email protected]
// 欄位: age, 值: 25

我們可以很輕鬆的就取得 key 以及對應的 value,也能按照插入的順序遍歷,語法上更為簡潔單純。

▼ 如果我們想動態更新某個欄位(如電子郵件),並檢查所有欄位是否都有填寫

// 更新某個欄位
formState.set('email', '[email protected]');

// 檢查是否有空值
let isValid = true;
for (let [field, value] of formState) {
  if (!value) {
    console.log(`錯誤:${field} 未填寫`);
    isValid = false;
  }
}

console.log('表單是否有效:', isValid);
// 輸出:
// 表單是否有效: true

使用 Map 可以輕鬆擴展 (例如使用 set 就能新增欄位),相比使用 Object,遍歷時可能需要額外處理屬性的順序,或是要轉成陣列,語法比較冗長也不直覺。

Map 能取代 Object 嗎

既然 Map 那麼好用,那大家一定會有這個疑問啦:Map 可以完全取代 Object 嗎?

聰明如你,一定知道答案會是「各有所長,不能完全取代,要看使用的情境」,對吧,不然我就不會寫這段了 XXD。

Map 的優勢

前面花了很大的篇幅反覆介紹 Map 的優點與特性,這邊就快速總結:

  • key 可以是任意型別,非常自由
  • 可迭代
  • 遍歷方便

Object 的優勢

Object 的存在一定有他的理由,這邊是我實際使用後,覺得比 Map 方便的地方:

  • 寫法簡單,適用於簡易的資料結構

▼ 如果我的資料結構不複雜,那使用 Object 會比 Map 快多了

// Object
const obj = { name: "Muki", age: 30 };

// Map
const map = new Map([["name", "Muki"], ["age", 30]])
  • 支援 JSON:Object 可以直接使用 JSON.stringify,但 Map 不行,Map 要先轉成陣列或物件才能使用,增加了程式碼的複雜度,也很容易轉換錯誤
const obj = { name: "Muki" };
console.log(JSON.stringify(obj)); // {"name": "Muki"}

const map = new Map([["name", "Muki"]]);
console.log(JSON.stringify([...map])); // [["name", "Muki"]]

Map 和 Object 適合的場景與應用

Map 不能完全取代 Object,但以下場景蠻適合使用 Map 的:

  • key 除了字串外需要其他類型,如物件,數字 ... 等
  • 需要明確的順序或方便遍歷
  • 資料量大且是動態資料

反過來說,Object 適合簡單不複雜的靜態資料,或有轉成 JSON 格式的需求。

未來使用時,可以先問問自己:「key 該怎麼設計?需要遍歷嗎?需要序列化嗎?資料複雜嗎?」,然後再挑選對應的寫法。

小結

這篇文章跟大家介紹了 Map 的使用方法,以及和 Object 之間的差異,希望能幫助大家更好的理解他們,以及選擇對應的使用工具。

關於 WeakMap 的介紹,因為文章篇幅太長了,所以我拆成上下兩篇,有興趣想要了解的朋友請繼續閱讀:JavaScript 中 Map 與 WeakMap 的介紹,以及和 Object 的差別 (2/2)

以上,有任何問題,都歡迎留言討論唷~。