跳到主要内容

WeakMap

一、认识


WeakMapES6 新增的一种弱映射集合类型,为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弱引用,在没有其他引用存在时垃圾回收能正确进行。

  • WeakMapObject 类型,原始数据类型是不能作为

六、引用


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里面的某个属性状态发生变化时,触发这个属性的多个回调函数。那么,如何有效的存储多个datadata里面多个属性以及属性的多个回调函数呢?

  • 通过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
});