Map
Node.js Map
Map 資料是 key / value
方式紀錄
- object 的 key 只能是
字串 (string)
,Map 的 key 則可以是任何
的資料型態 - Map 中的資料是有序的,當你遍歷一個 Map 資料結構時,會依照
key-value pairs
先前寫入的順序 (insertion order)。
函式資料
變數 | 說明 |
---|---|
Map.size | 元素數量 |
Map.has() | 是否有此鍵值資料 |
Map.get() | 取得鍵值資料 |
Map.set() | 設定鍵值資料 |
Map.delete() | 刪除鍵值資料 |
Map.clear() | 刪除全部資料 |
Map.keys() | 取得全部鍵值資料 |
Map.values() | 取得全部數值資料 |
Map.forEach() | 遍歷 Map 資料 |
Map.entries() | 取得 [key, value] 資料 |
Map 允許使用函數、物件、其他類型作為 key 值
set(key, value)
const func = () => null;
const object = {};
const array = [];
const bool = false;
const map = new Map();
map.set(func, 'value1');
map.set(object, 'value2');
map.set(array, 'value3');
map.set(bool, 'value4');
map.set(NaN, 'value5');
// Map(5) {
// [Function: func] => 'value1',
// {} => 'value2',
// [] => 'value3',
// false => 'value4',
// NaN => 'value5'
// }
console.log(map);
遍歷元素
物件需要透過 Object.keys
or Object.values
or Object.entries
,或者 for..in
去做循環遍歷元素
Map 可以直接遍歷,使用 for..of
or forEach
for(let [key, value] of map){
console.log(key)
console.log(value)
}
map.forEach((key,value)=>{
console.log(key)
console.log(value)
})
Map 與 Object 效率比較
查詢鍵值效率
let obj = {}, map = new Map(), n = 1000000
for(let i = 0 ; i < n ; i++){
obj[i] = i
map.set(i, i)
}
let result;
console.time('Object');
result = obj.hasOwnProperty('999999');
console.timeEnd('Object');
// Object: 0.250ms
console.time('Map');
result = map.has(999999);
console.timeEnd('Map');
// Map: 0.095ms (2.6 times faster)
新增效率
console.time('Object');
obj[n] = n;
console.timeEnd('Object');
// Object: 0.229ms
console.time('Map');
map.set(n, n);
console.timeEnd('Map');
// Map: 0.005ms (45.8 times faster!)
刪除效率
console.time('Object');
delete obj[n];
console.timeEnd('Object');
// Object: 0.376ms
console.time('Map');
map.delete(n);
console.timeEnd('Map');
// Map: 0.012ms (31 times faster!)
for loop 效率
Map在 for loop 下較慢
console.time('Object');
for (let i = 0; i < n; i++) {
obj[i] = i;
}
console.timeEnd('Object');
// Object: 32.143ms
let obj = {}, map = new Map(), n = 1000000;
console.time('Map');
for (let i = 0; i < n; i++) {
map.set(i, i);
}
console.timeEnd('Map');
// Map: 163.828ms (5 times slower)
Map 缺點
賦值
和搜索
操作都是O(n)
的時間複雜度(n 是鍵值對的個數)- 因為這兩個操作都需要遍歷全部整個陣列來進行匹配
- 可能會導致
內存洩漏(Memory Leak)
,因為陣列會一直引用著每個鍵和值。這種引用使得垃圾回收算法不能回收處理他們,即使沒有其他任何引用存在了。
WeakMap
WeakMap 特性
- 只接受
object
當作鍵值(key),null 也不能當鍵值 - 基本資料型態 (primitive data types) 都
不能被當作是 key
- WeakMap 中的 key 所指向的 object
不會被垃圾回收機制 (garbage collection)
計入參考- weak: 弱引用 (weakly reference)
- WeakMap 中的 object 可能隨時會被自動回收 (garbage collected),而當 object 被回收後,其所對應的 key-value pair 也會自動被刪除。
- value 可以是任何型態,包含像 object 或 function
- key 和 value 可以是任何物件,甚至是個 WeakMaps
var wm = new WeakMap();
// 錯誤
// TypeError: Invalid value used as weak map key
wm.set('a', 1);
// 錯誤
// TypeError: Invalid value used as weak map key
wm.set(101, 2);
const wm1 = new WeakMap(),
wm2 = new WeakMap()
const o1 = {},
o2 = function() {};
wm1.set(o1, 123);
wm1.set(o2, 'abc');
// value 可以是任何型態,包含像 object 或 function
wm2.set(o1, o2);
// key 和 value 可以是任何物件,甚至是個 WeakMaps
wm2.set(wm1, wm2);
WeakMap 使用情境
- WeakMap 的典型運用之一是引用 DOM 元素物件,當 DOM 元素被移除後,對應的 WeakMap 紀錄也會自動被移除,所以說用 WeakMap 可以方便地避免
內存泄露 (memory leak)
的問題。
使用 DOM 當作 WeakMap 的 key
let myBtn = document.getElementById('btn');
let wm = new WeakMap();
wm.set(myBtn, {clickCnt: 0});
myBtn.addEventListener('click', function() {
let btnData = wm.get(myBtn);
btnData.clickCnt++;
}, false);