WeakMap
弱引用
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的,WeakMap 的 key 是不可枚举的。
Why WeakMap ?
在 JavaScript 里,map API 可以 通过使其四个 API 方法共用两个数组(一个存放键,一个存放值)来实现。给这种 map 设置值时会同时将键和值添加到这两个数组的末尾。从而使得键和值的索引在两个数组中相对应。当从该 map 取值的时候,需要遍历所有的键,然后使用索引从存储值的数组中检索出相应的值。
但这样的实现会有两个很大的缺点:
首先赋值和搜索操作都是 O(n) 的时间复杂度(n 是键值对的个数),因为这两个操作都需要遍历全部整个数组来进行匹配。
另外一个缺点是可能会导致内存泄漏,因为数组会一直引用着每个键和值。这种引用使得垃圾回收算法不能回收处理他们,即使没有其他任何引用存在了。
场景一(事件处理的绑定)
const btn1 = document.querySelector("#btn1");
const btn2 = document.querySelector("#btn2");
btn1.addEventListener("click", handleBtn1Click);
btn2.addEventListener("click", handleBtn2Click);
function handleBtn1Click() {}
function handleBtn2Click() {}
// 当不需要btn1 和 btn2 两个节点的失火,因为绑定了监听函数,所以不会被垃圾回收机制回收
btn1.remove();
btn2.remove();
// 必须置空才能被回收
handleBtn1Click = null
handleBtn2Click = null
使用WeakMap
WeakMap键名是弱引用,键名都被回收了,键值一并被回收,则不需要再对 handleBtn1Click handleBtn2Click 进行清空
const btn1 = document.querySelector("#btn1");
const btn2 = document.querySelector("#btn2");
const btnMap = new WeakMap()
// 弱引用 btn1 btn2 没被引用则自动回收
btnMap.set(btn1,handleBtn1Click)
btnMap.set(btn2,handleBtn2Click)
btn1.addEventListener("click", btnMap.get(btn1));
btn2.addEventListener("click", btnMap.get(btn2));
function handleBtn1Click() {}
function handleBtn2Click() {}
btn1.remove();
btn2.remove();
场景二 (解决深拷贝死循环问题)
let test1 = {};
let test2 = {};
test2.test1 = test;
test1.test2 = test2;
使用WeakMap来记录是否拷贝过
function deepClone(origin, hashMap = new WeakMap()) {
if (typeof origin == undefined && typeof origin !== "object") {
return origin;
}
if (origin instanceof Date) {
return new Date(origin);
}
if (origin instanceof RegExp) {
return new RegExp(origin);
}
const hashKey = hashMap.get(origin);
if (hashKey) return hashKey;
const target = new origin.constructor();
hashMap.set(origin, target);
for (const k in origin) {
if (origin.hasOwnProperty(k)) {
target[k] = deepClone(origin[k], hashMap);
}
}
return target;
}