WeakMap
一、认识
WeakMap 是ES6
新增的一种弱映射集合类型,为JavaScript
带来了增强的键/值对存储机制。WeakMap是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。WeakMap 持有的是每个键对象的弱引用,所以WeakMap 中的键/值任何时候都可能被销毁,所以没必要提供迭代其键/值的能力。
二、语法
2.1 new WeakMap()
const weakMap = new WeakMap();
console.log(weakMap); // WeakMap {}
2.2 new WeakMap([[对象: 任意JavaScript数据类型]])
注意: 弱映射中的键必须是 Object
或者 继承自Object
的类型,如果使用非对象设置键会抛出异常 原因: 为了保证只有通过值对象的引用才能取得其值。如果允许基本数据类型设置其值,那么没办法区分初始化时使用的字符串字面量
和初始化后使用的一个相等的字符串
了。
const key1 = {id:1};
const key2 = {id:2};
const weakMap = new WeakMap([[key1,'啦啦啦'],[key2,'哈哈哈']]);
console.log(weakMap); // WeakMap {{…} => '哈哈哈', {…} => '啦啦啦'}
三、属性
四、场景
4.1 weakMap.set()
weakMap.set() 新增键值对,并且返回WeakMap实例,因此**WeakMap实例.set()**可以链式调用。注意: 弱映射中的键必须是 Object
或者 继承自Object
的类型,如果使用非对象设置键会抛出异常
语法
const weakMap = new WeakMap();
const key1 = {id:1};
const key2 = {id:2};
weakMap.set(key1,'哈哈哈');
weakMap.set(key2,'啦啦啦');
console.log(weakMap); // WeakMap {{…} => '哈哈哈', {…} => '啦啦啦'}
const weakMap = new WeakMap();
const key1 = {id:1};
const key2 = {id:2};
weakMap.set(key1,'哈哈哈').set(key2,'啦啦啦');
console.log(weakMap); // WeakMap {{…} => '哈哈哈', {…} => '啦啦啦'}
4.2 weakMap.get()
语法
const weakMap = new WeakMap();
const key1 = { id: 1 };
const key2 = { id: 2 };
weakMap.set(key1, "哈哈哈").set(key2, "啦啦啦");
console.log(weakMap.get(key1)); // 哈哈哈
4.3 weakMap.has()
语法
const weakMap = new WeakMap();
const key1 = { id: 1 };
const key2 = { id: 2 };
weakMap.set(key1, "哈哈哈").set(key2, "啦啦啦");
console.log(weakMap.has(key1)); // true
4.4 weakNap.delete()
weakMap.delete() 删除WeakMap
中的指定键,返回一个布尔值,表示映射中是否存在要删除的键值对。
语法
const weakMap = new WeakMap();
const key1 = { id: 1 };
const key2 = { id: 2 };
weakMap.set(key1, "哈哈哈").set(key2, "啦啦啦");
console.log(weakMap.delete(key1)); // true
console.log(weakMap); // WeakMap {{…} => '啦啦啦'}
4.5 weakMap.clear()
weakMap 持有的是每个键对象的弱引用,WeakMap 中的键/值任何时候都可能被销毁,所以没必要提供 clear()
一次性销毁所有键值的方法。
五、对比
5.1 Map Vs WeakMap
Map:
- Map 的 键 可以是任何值(对象或者原始值)
WeakMap:
-
WeakMap 的 键 是弱引用,在没有其他引用存在时垃圾回收能正确进行。
-
WeakMap 的 键 是 Object 类型,原始数据类型是不能作为 键 的
六、引用
6.1 强引用
强引用 会影响垃圾回收机制,存在强引用的对象永远不会触发垃圾回收机制回收。Map
键为强引用,如下所示 当 obj
置为 null
时,map
键还对 obj
的地址保持引用,所以不会触发垃圾回收机制回收
const registry = new FinalizationRegistry((heldValue)=>{
console.log('恭喜您,内存回收成功!',heldValue); // 回调函数是不会执行的,因为没有触发垃圾回收机制回收
});
let obj = {
type:'对象',
data:[]
}
registry.register(obj,'obj销毁');
obj = null;
6.2 弱引用
弱引用 不会影响垃圾回收机制。WeakMap
键为弱引用,如下所示 当 obj
置为 null
时,WeakMap
键没有对 obj
的地址保持引用,所以会触发垃圾回收机制回收
const registry = new FinalizationRegistry((heldValue)=>{
console.log('恭喜您,内存回收成功!',heldValue); // 等待一段时间后会输出
});
let obj = {
type:'对象',
data:[]
}
const weakMap = new WeakMap([[obj,'weakMap为弱引用']]);
registry.register(obj,'obj销毁');
obj = null;
七、场景
7.1 Vue3.0 reactiveMap
Vue3.0依赖收集的过程中,每一个组件都有自己的data状态,每一个data里面的都有很多的属性要管理。当data里面的某个属性状态发生变化时,触发这个属性的多个回调函数。那么,如何有效的存储多个data和data里面多个属性以及属性的多个回调函数呢?
-
通过
WeakMap
数据解构存储多个data(也就是多个对象)
中的属性与回调函数关系。这里使用WeakMap的原因: 当其中一个对象销毁后,由于WeakMap弱引用的特性,可以直接通过垃圾回收机制回收掉。如果这里使用Map的话,这个对象销毁后,那Map里面还是会保持引用的,不可以触发垃圾回收机制回收。 -
通过
Map
数据解构管理data
属性与回调函数的关系。这里使用Map的原因: 因为data
里的属性有原始数字据的。
const obj1 = {
type: "obj1",
data: [],
};
const obj2 = {
type: "obj2",
like: [],
};
function obj1TypeFunc1() {
console.log("obj1 type 监听回调函数1");
}
function obj1TypeFunc2() {
console.log("obj1 type 监听回调函数2");
}
function obj1DataFunc1() {
console.log("obj1 data 监听回调函数1");
}
function obj1DataFunc2() {
console.log("obj1 data 监听回调函数2");
}
function obj2TypeFunc1() {
console.log("obj2 type 监听回调函数1");
}
function obj2TypeFunc2() {
console.log("obj2 type 监听回调函数2");
}
function obj2DataFunc1() {
console.log("obj2 data 监听回调函数1");
}
function obj2DataFunc2() {
console.log("obj2 data 监听回调函数2");
}
// obj1 依赖收集
const objWeakMap = new WeakMap();
const obj1Map = new Map();
obj1Map.set("type", [obj1TypeFunc1, obj1TypeFunc2]);
obj1Map.set("data", [obj1DataFunc1, obj1DataFunc2]);
objWeakMap.set(obj1, obj1Map);
// obj2 依赖收集
const obj2Map = new Map();
obj2Map.set("type", [obj2TypeFunc1, obj2TypeFunc2]);
obj2Map.set("data", [obj2DataFunc1, obj2DataFunc2]);
objWeakMap.set(obj2, obj2Map);
// 当 obj1.type 状态发生变化
obj1.type = "obj1发生变化";
const targetMap = objWeakMap.get(obj1);
const watchCallbackList = targetMap.get("type");
watchCallbackList.forEach((fun) => {
fun(); // obj1 type 监听回调函数1 obj1 type 监听回调函数2
});