一文搞懂 ES6 的 Map,Map 和 Object 如何選擇

什麼是 Map

Map 是 ECMAScript 6 的新增特性,是一種新的集合類型,爲 javascript 帶來了真正的鍵 / 值存儲機 制。

Map 的基本 API

1、new Map() 創建新的 Map 對象

使用 new 關鍵字和 Map 構造函數可以創建一個空映射:

// 創建新的 Map 對象。
const m = new Map();

如果想在創建的同時初始化實例,可以給 Map 構造函數傳入一個可迭代對象,需要包含鍵 / 值對數 組。可迭代對象中的每個鍵 / 值對都會按照迭代順序插入到新映射實例中

const m1 = new Map([ 
 ["key1""val1"], 
 ["key2""val2"], 
 ["key3""val3"] 
]); 
alert(m1.size)  // 3
console.log(m1)

console 打印出的 m1 如下:

使用自定義迭代器初始化映射

// 使用自定義迭代器初始化映射
const m2 = new Map({ 
 [Symbol.iterator]function*() { 
 yield ["key1""val1"]; 
 yield ["key2""val2"]; 
 yield ["key3""val3"]; 
 } 
}); 
alert(m2.size); // 3
console.log(m2)

console 打印出的 m2 如下:

set()、 get()、has()、delete()、clear()

初始化之後,可以使用 set() 方法再添加鍵 / 值對。另外,可以使用 get() 和 has() 進行查詢,可 以通過 size 屬性獲取映射中的鍵 / 值對的數量,還可以使用 delete() 和 clear() 刪除值。

const m = new Map(); 
alert(m.has("firstName")); // false 
alert(m.get("firstName")); // undefined 
alert(m.size); // 0 
m.set("firstName""Matt") 
 .set("lastName""Frisbie"); 
alert(m.has("firstName")); // true 
alert(m.get("firstName")); // Matt 
alert(m.size); // 2 
m.delete("firstName"); // 只刪除這一個鍵/值對
alert(m.has("firstName")); // false 
alert(m.has("lastName")); // true 
alert(m.size); // 1 
m.clear(); // 清除這個映射實例中的所有鍵/值對
alert(m.has("firstName")); // false 
alert(m.has("lastName")); // false 
alert(m.size); // 0

set() 方法返回映射實例,因此可以把多個操作連綴起來,包括初始化聲明:

const m = new Map().set("key1""val1");
m.set("key2""val2")
    .set("key3""val3");
alert(m.size); // 3

與 Object 只能使用數值、字符串或符號作爲鍵不同,Map 可以使用任何 JavaScript 數據類型作爲 鍵。

const m = new Map(); 
const functionKey = function() {}; 
const symbolKey = Symbol(); 
const objectKey = new Object(); 
m.set(functionKey, "functionValue"); 
m.set(symbolKey, "symbolValue"); 
m.set(objectKey, "objectValue"); 
alert(m.get(functionKey)); // functionValue 
alert(m.get(symbolKey)); // symbolValue 
alert(m.get(objectKey)); // objectValue

Map 的順序與迭代

與 Object 類型的一個主要差異是,Map 實例會維護鍵值對的插入順序,因此可以根據插入順序執 行迭代操作。Map 映射實例可以提供一個迭代器(Iterator),能以插入順序生成 [key, value] 形式的數組。

entries() 返回 Map 對象中鍵 / 值對的數組。

Map 映射實例提供一個迭代器(Iterator),能以插入順序生成 [key, value] 形式的數組。可以 通過 entries()方法(或者 Symbol.iterator 屬性,它引用 entries())取得這個迭代器:

const m = new Map([ 
 ["key1""val1"], 
 ["key2""val2"], 
 ["key3""val3"] 
]); 
alert(m.entries === m[Symbol.iterator]); // true 
for (let pair of m.entries()) { 
 alert(pair); 
} 
// [key1,val1] 
// [key2,val2] 
// [key3,val3] 
for (let pair of m[Symbol.iterator]()) { 
 alert(pair); 
} 
// [key1,val1] 
// [key2,val2] 
// [key3,val3]

因爲 entries() 是默認迭代器,所以可以直接對映射實例使用擴展操作,把映射轉換爲數組:

const m = new Map([ 
 ["key1""val1"], 
 ["key2""val2"], 
 ["key3""val3"] 
]); 
console.log([...m]); // [[key1,val1],[key2,val2],[key3,val3]]

如果不使用迭代器,而是使用回調方式,則可以調用映射的 forEach(callback, opt_thisArg) 方法並傳入回調,依次迭代每個鍵 / 值對。

const m = new Map([ 
 ["key1""val1"], 
 ["key2""val2"], 
 ["key3""val3"] 
]); 
m.forEach((val, key) => alert(`${key} -> ${val}`)); 
// key1 -> val1 
// key2 -> val2 
// key3 -> val3
keys() 和 values() 分別返回以插入順序生成鍵和值
const m = new Map([
    ["key1""val1"],
    ["key2""val2"],
    ["key3""val3"]
]);
for (let key of m.keys()) {
    alert(key);
}
// key1 
// key2 
// key3 
for (let key of m.values()) {
    alert(key);
}
// value1 
// value2 
// value3

選擇 object 還是 map

Map 的大多數特性都可以通過 Object 類型實現,但二者之間還是存在一些細微的差異。對於大部分業務開發者來說,選擇 object 還是 map 只是個人喜好問題,其實影響不大。但是對於追求業務和性能的開發者來說,object 和 map 確實存在很大的區別。在具體實踐中使用哪一個,還是值得細細甄別。

1. 內存佔用

同一瀏覽器中給定固定大小的內存,Map 大約可以比 Object 多存儲 50% 的鍵 / 值對。

2. 插入性能

向 Object 和 Map 中插入新鍵 / 值對的消耗大致相同,不過插入 Map 在所有瀏覽器中一般會稍微快 一點兒。對這兩個類型來說,插入速度並不會隨着鍵 / 值對數量而線性增加。如果代碼涉及大量插入操 作,那麼顯然 Map 的性能更佳。

3. 查找速度

與插入不同,從大型 Object 和 Map 中查找鍵 / 值對的性能差異極小,但如果只包含少量鍵 / 值對, 則 Object 有時候速度更快。如果代碼涉及大量查找操作,那麼某些情況下可能選 擇 Object 更好一些。

4、刪除性能

對大多數瀏覽器引擎來說,Map 的 delete() 操作都比插入和查找更快。如果代碼涉及大量刪除操作,那麼毫無疑問應該選擇 Map。

文章參考自《JavaScript 高級程序設計第四版》

微信號:javascriptyishu

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/n-lxVoHpqNzaswjKbrlrFg