我平常儲存資料還是習慣用 Object (物件),但最近發現了 Map 和 WeakMap 這兩個資料結構,相比 Object,他們更靈活也更好用。文章也會跟大家介紹 Map 和 WeakMap 這兩種資料結構,並深入介紹他們與 Object 的差異,歡迎一起加入使用(招手
什麼是 Map
Map 是 ES6 推出的資料結構,跟 Object 一樣也是 key-value (鍵值對) 的集合,但卻更為靈活,Map 的特點如下:
- key 可以是任意型別:
Object的 key 只能是string或Symbol,即使我們給Object如數字的 key155,也是會強制轉成 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 已存在會自動更新 value | map.set('a', 1); // Map { 'a' => 1 } |
get(key) | 取得指定 key 的 value,若 key 不存在則返回 undefined | map.get('a'); // 1 |
has(key) | 檢查指定的 key 是否存在,返回 Boolean | map.has('a'); // true |
delete(key) | 刪除指定的 key 及其 value,會返回 Boolean 表示是否刪除成功 | map.delete('a'); // true |
clear() | 移除 Map 中的所有 key-value | map.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 function | map.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}`);
}範例:使用 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)
以上,有任何問題,都歡迎留言討論唷~。