Photo by Mohammad Rahmani on Unsplash
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);
Reference
- JS Map | Explosion Units
- 如何利用JavaScript的Map提升性能 - 掘金
- ES6 Map/WeakMap 物件 - JavaScript (JS) 教学 Tutorial
- WeakMap - JavaScript | MDN
Donate KJ 贊助作者喝咖啡
如果這篇文章對你有幫助的話,可以透過下面支付方式贊助作者喝咖啡,如果有什麼建議或想說的話可以贊助並留言給我
If this article has been helpful to you, you can support the author by treating them to a coffee through the payment options below. If you have any suggestions or comments, feel free to sponsor and leave a message for me!
方式 Method | 贊助 Donate |
PayPal | https://paypal.me/kejyun |
綠界 ECPay | https://p.ecpay.com.tw/AC218F1 |
歐付寶 OPay | https://payment.opay.tw/Broadcaster/Donate/BD2BD896029F2155041C8C8FAED3A6F8 |