ES6之WeakMap
在Vue3的源码中,WeakMap被广泛应用于缓存对象。WeakMap提供了类似于Map的功能,但是键是弱引用的,也就是说当键对应的对象不再被其他对象所引用时,这个键值对会自动被GC(垃圾回收)回收
什么是 WeakMap
- WeakMap 对象是一组键值对的集合,其中的键是 弱引用 的。其键必须是 对象,而值可以是任意的。
语法
- Iterable 是一个数组(二元数组)或者其他可迭代的且其元素是键值对的对象。每个键值对会被加到新的 WeakMap 里。
new WeakMap([iterable])
方法
- WeakMap 有四个方法:分别是 get、set、has、delete
const wm1 = new WeakMap(),
wm2 = new WeakMap(),
wm3 = new WeakMap();
const o1 = {},
o2 = function() {},
o3 = window;
wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // value 可以是任意值,包括一个对象或一个函数
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个 WeakMap 对象
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined,wm2 中没有 o2 这个键
wm2.get(o3); // undefined,值就是 undefined
wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即使值是 undefined)
wm3.set(o1, 37);
wm3.get(o1); // 37
wm1.has(o1); // true
wm1.delete(o1);
wm1.has(o1); // false
WeakMap的优点
相比较而言,在
Map
中保存了一些对象的引用,即使这些对象在其他地方都已经不再使用,但是由于它们仍被Map
引用,所以它们不能被垃圾回收,这就可能导致内存泄漏
-
**减少内存泄漏:**因为WeakMap的键是弱引用的,当键不再被其他对象引用时,WeakMap 会自动释放对应的键值对,从而避免内存泄漏
-
**自动释放不再需要的对象:**由于WeakMap的弱引用特性,不再需要这些数据时,它们会被自动释放,不会造成内存泄漏
-
**提高性能:**不会阻止垃圾回收,避免占用过多的内存
WeakMap的缺点
- 不支持遍历
- 键必须是对象
WeakMap应用场景
解决内存泄漏问题
- 内存泄漏是指程序中不再使用的对象仍然保留在内存中,导致内存占用过高,甚至可能导致程序崩溃。WeakMap 可以用来解决这个问题,因为它的键是弱引用的,当键不再被其他对象引用时,WeakMap 会自动释放对应的键值对,从而避免内存泄漏。
// 示例:使用 WeakMap 解决内存泄漏问题
class LeakyClass {
constructor() {
this.data = new Map();
}
// 添加数据
setData(key, value) {
this.data.set(key, value);
}
// 获取数据
getData(key) {
return this.data.get(key);
}
// 移除数据
removeData(key) {
this.data.delete(key);
}
}
// 创建 LeakyClass 的实例
const leakyObject = new LeakyClass();
// 将对象添加到 WeakMap 中,并将其作为键
const weakMap = new WeakMap();
weakMap.set(leakyObject, 'some data');
// 断开对 leakyObject 的引用
leakyObject = null;
// 检查 WeakMap 中是否仍然存在对应键值对
console.log(weakMap.has(leakyObject));
- 们创建了一个LeakyClass类,它有一个data属性,用于存储数据。我们将leakyObject添加到weakMap中,并将其作为键。然后,我们断开对leakyObject的引用,此时leakyObject成为垃圾回收的候选对象。最后,我们检查weakMap中是否仍然存在对应键值对。由于leakyObject已经不再被引用,它将被垃圾回收,因此weakMap.has(leakyObject)返回false。
避免循环引用
- 循环引用是指两个或多个对象之间相互引用,形成一个循环,导致这些对象无法被垃圾回收。WeakSet 可以用来避免循环引用,因为它的成员是弱引用的,不会阻止垃圾回收。
// 示例:使用 WeakSet 避免循环引用
class Node {
constructor(value) {
this.value = value;
this.children = new WeakSet();
}
addChild(node) {
this.children.add(node);
}
removeChild(node) {
this.children.delete(node);
}
}
// 创建两个 Node 对象,并形成循环引用
const node1 = new Node(1);
const node2 = new Node(2);
node1.addChild(node2);
node2.addChild(node1);
// 将 node1 添加到 WeakSet 中
const weakSet = new WeakSet();
weakSet.add(node1);
// 断开对 node1 的引用
node1 = null;
// 等待垃圾回收
setTimeout(() => {
// 检查 WeakSet 中是否仍然存在 node1
console.log(weakSet.has(node1));
}, 1000);
- 我们创建了两个
Node
对象,并通过addChild
方法形成循环引用。然后,我们将node1
添加到weakSet
中。最后,我们断开对node1
的引用,并等待垃圾回收。在垃圾回收之后,weakSet.has(node1)
返回false
,因为node1
已经被回收
临时数据存储
- WeakMap 和 WeakSet 可以用于存储临时数据,这些数据只在特定的时间段内有用。由于它们的弱引用特性,当不再需要这些数据时,它们会被自动释放,不会造成内存泄漏。
// 示例:使用 WeakMap 存储临时数据
const weakMap = new WeakMap();
// 创建一个临时对象,并将其添加到 WeakMap 中
const tempObject = {key: 'value'};
weakMap.set(tempObject, 'some data');
// 使用临时对象
console.log(weakMap.get(tempObject));
// 断开对临时对象的引用
tempObject = null;
// 等待垃圾回收
setTimeout(() => {
// 检查 WeakMap 中是否仍然存在对应键值对
console.log(weakMap.has(tempObject));
}, 1000);
我们使用WeakMap
存储了一个临时对象和相关的数据。然后,我们断开对临时对象的引用,并等待垃圾回收。最后,WeakMap.has(tempObject)
返回false
,因为临时对象已经被回收
缓存
- WeakMap 和 WeakSet 也可以用于缓存数据,尤其是在一些数据可能会变得很大的情况下。由于它们的弱引用特性,可以确保在不再需要这些数据时,它们会被自动释放,避免占用过多的内存
// 示例:使用 WeakMap 进行缓存
const weakMap = new WeakMap();
// 创建一个大型对象,并将其添加到 WeakMap 中
const largeObject = {key: 'value'};
weakMap.set(largeObject, 'cached data');
// 使用缓存的数据
console.log(weakMap.get(largeObject));
// 断开对大型对象的引用
largeObject = null;
// 等待垃圾回收
setTimeout(() => {
// 检查 WeakMap 中是否仍然存在对应键值对
console.log(weakMap.has(largeObject));
}, 1000);
- 我们使用
WeakMap
缓存了一个大型对象。然后,我们断开对大型对象的引用,并等待垃圾回收。最后,WeakMap.has(largeObject)
返回false
,因为大型对象已经被回收
保存对象的私有数据
-
正式由于WeakMap的不可遍历的缺点,可以利用这个特性来存储一些只有特定代码能够访问的数据
let privateData = new WeakMap(); function MyClass() { privateData.set(this, { secret: 'my secret data', }); } MyClass.prototype.getSecret = function() { return privateData.get(this).secret; }; let obj = new MyClass(); console.log(obj.getSecret()); // 输出: 'my secret data'
好文推荐
- https://www.jb51.net/javascript/306773luq.htm