帶鍵(key)的集合
Map、WeakMap(映射) 和 Set(集合) 對象承載的數據元素可以按照插入時的順序被迭代遍歷。
Map
與Object的比較
Map的key值可以是任意類型(可以將原始值(字面量)存儲爲鍵),Object只能是字符串或Symbol。
Map可通過size獲取數量(length=0),Object只能手動計算。
Map遍歷遵循數據的插入順序,Object自動排序。
Map可作爲迭代器被迭代,Object需要其他方式(for in )迭代
使用
let map = new Map(); // 創建空的map
map.set(1, 'map中key爲1的value'); // 添加或修改
console.log(map.get(1)); // 獲取,返回value或undefined
console.log(map.has(1)); // 判斷是否包含,返回Boolean
map.delete(1); // 刪除
let obj = {
name: '喬峯'
};
let map2 = new Map([[1, 'map2中key爲1的value'], [2, obj]]); // 創建並初始化map,初始化格式[[key, value],[key, value]...]
console.log(map2.size); // 獲取數量
let map3 = new Map(map2); // 淺拷貝
console.log(map3 == map2); // false
let map3_2 = map3.get(2);
map3_2.name = '虛竹';
console.log(obj); // {name:"虛竹"}
map3.clear(); // 清空
鍵的相等
// get的依據類似'===',雖然NaN!==NaN,但Map.get(NaN)可以被獲取到
// 注意:+0 === -0
// Map可以設置鍵爲NaN null undefined並獲取
let map = new Map();
let obj = {};
map.set(NaN,1);
map.set({},2);
map.set(obj,3);
console.log(map.get(NaN)); // 1
console.log(NaN===NaN); // false
console.log(map.get({})); // undefined
console.log({}==={}); // false
console.log(map.get(obj)); // 3
console.log(obj===obj); // true
遍歷
let map = new Map([
[1, 'key爲1的value'],
[2, 'key爲2的value'],
[3, 'key爲3的value']
]);
let entries = map.entries(); // 返回按插入順序包含key/value映射的Iterator迭代器對象
let keys = map.keys(); // 返回按插入順序包含key的Iterator迭代器對象
let values = map.values(); // 返回按插入順序包含value的Iterator迭代器對象
// for of遍歷
for (let [key,value] of map) {}
for (let [key,value] of entries) {}
for (let key of keys) {}
for (let value of values) {}
// forEach遍歷(Iterator不能用forEach遍歷)
map.forEach((value,key) => {});
與數組交互
創建和轉化
let arr = [
[1, 'key爲1的value'],
[2, 'key爲2的value'],
[3, 'key爲3的value']
];
let map = new Map(arr); // Array轉Map
let arr2 = Array.from(map); // Map轉Array
let arr3 = [...map]; // 效果同arr2
console.log([...map]); // 展開運算符本質上是將Map對象轉換成數組,打印同arr
// 補充說明:
// Array.from從一個類似數組或可迭代對象,創建一個新的淺拷貝的數組
let arrLike = {
0: 'key爲0的value',
1: 'key爲1的value',
2: 'key爲2的value',
length: 3
}; // 類似數組對象
console.log(Array.from(arrLike)); // ['key爲0的value', 'key爲1的value', 'key爲2的value']
合併:Map與Map或數組對象可以合併,但是會保持鍵的唯一性,同鍵被覆蓋。
let first = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let second = new Map([
[1, 'uno'],
[2, 'dos']
]);
// 合併兩個Map對象時,如果有重複的鍵值,則後面的會覆蓋前面的。
let merged = new Map([...first, ...second]);
// 合併Map和數組時,如果有重複的鍵值,則後面的會覆蓋前面的。
let merged2 = new Map([...first, [3, 'ent']]);
console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
console.log(merged2.get(3)); // ent
規範
使用對象屬性賦值方法可以給Map添加屬性,但不會被Map的方法識別
let map = new Map();
map['age'] = 18;
console.log(map.get('age')); // undefined
map.delete('age'); // 並沒有刪除什麼
console.log(map.age); // 18
WeakMap
與Map的區別
1.key必須是對象類型(Object),不能是基礎數據類型
2.key鍵所引用(指向)的對象是弱引用。注意不是value值指向的對象。
3.Map是可迭代的(iterable),WeakMap是不可迭代的(key是弱引用)。
Map原理(個人理解)
Map會創建兩個數組,一個存放鍵[…keys],一個存放值[…values],使用其API共用兩個數組。
當從Map中取(get)某個值或修改(set)某個值時,Map會遍歷所有的鍵[…keys],找到後利用索引再去獲取值[…values]。這樣有兩個缺點:
1.賦值和搜索都需要遍歷,消耗時間和內存。
2.[…keys]和[…values]會一直保留對key value的引用,這種引用使得垃圾回收算法不能回收處理他們,即使沒有其他任何引用存在了。也就是會造成內存泄漏。
弱引用
解釋1:在計算機程序設計中,弱引用與強引用相對,是指不能確保其引用的對象不會被垃圾回收器回收的引用。一個對象若只被弱引用所引用,則被認爲是不可訪問(或弱可訪問)的,並因此可能在任何時刻被回收。
解釋2:垃圾回收機制不考慮對該對象的引用。如果對象沒有在其他地方被引用,則垃圾回收機制會回收該對象所佔用的內存。
解釋3:WeakMap可以把一個對象所關聯的數據和該對象的生命週期聯繫起來。當對象被銷燬,其關聯的數據也被釋放。
// 個人理解舉例
let obj = {
name:'喬峯'
};
let obj2 = {
name:'虛竹'
};
let map = new Map();
map.set(obj, 1);
let weakMap = new WeakMap();
weakMap.set(obj2, 1);
obj = null; // 手動刪除全局對{name:'喬峯'}的引用
obj2 = null; // 手動刪除全局對{name:'虛竹'}的引用
setTimeout(()=>{
console.log(map); // Map(1) {{…} => 1}
console.log(weakMap); // WeakMap {}
}, 10000); // 設置時間長點,等待垃圾回收
使用
// 弱引用不能被枚舉。所以WeakMap不能被遍歷
let obj = {};
let weakMap = new WeakMap(); // 創建
weakMap.set(obj,1); // 添加/修改
console.log(weakMap.get(obj)); // 獲取
console.log(weakMap.has(obj)); // 判斷是否包含
weakMap.delete(obj); // 刪除
// WeakMap的key是弱引用,所以WeakMap是不可被迭代的
// 下面代碼報錯:weakMap is not iterable
for(let [key, value] of weakMap) {}
// weakMap.clear()已被廢棄,可重新創建一個空WeakMap替換來實現清空
weakMap = new WeakMap();
應用場景
1.給Dom添加數據或註冊監聽事件
let myImg = document.getElementById('myImg');
let weakMap = new WeakMap();
weakMap.set(myImg, {name:'',description:'',size:''});
// 或者
weakMap.set(myImg, function() {
...handler
});
myImg.addEventListener('click', weakMap.get(myImg));
// 當myImg元素在頁面中被註銷(刪除)後,weakMap綁定的數據或回調方法也會被垃圾回收機制處理
2.數據緩存
3.私有屬性
參考
MDN:
Keyed collections
Map MDN
WeakMap MDN
其他:
ES6 系列之 WeakMap
WeakMap的應用場景