什么是 WeakMap ?
WeakMap 提供了一种从外部扩展对象而不干扰垃圾收集的方法。
它是一个 Map 字典,其中的键很弱,也就是说,如果对该键的所有引用都丢失,并且不再有对该值的引用,则可以对该值进行垃圾回收。
基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。
需要注意的是,WeakMap 的 key 只能是 Object 类型。 原始数据类型是不能作为 key 的(比如 Symbol)。
WeakMap VS Map
假设一个以下场景:
要求:如果A对象 作为B对象的键、值或子元素时,将A对象引用设置为 null 时,该对象是不会被回收的,依然存在
例如
var a = {};
var map = new Map();
map.set(a, '学前端')
a = null;
console.log(map.keys()) // MapIterator {{}}
console.log(map.values()) // MapIterator {"学前端"}
如果想让 a 置为 null 时,该对象被回收,该怎么做呢?
ES6 考虑到了这一点,推出了: WeakMap 。它对于值的引用都是不计入垃圾回收机制的,所以名字里面才会有一个"Weak",表示这是弱引用(对对象的弱引用是指当该对象应该被GC回收时,不会阻止GC的回收行为)(就是说不会管你引用多少次,我照样收)
var a = {};
var map = new WeakMap();
map.set(a, '学前端')
map.get(a)
a = null;
console.log(map.get('a')) // undefind
//`WeakMap` 不能被遍历,用不了 .keys .values 方法
真实的场景的一个例子
WeakMap 可以用来从外部扩展对象,让我们再一起来看一个现实世界中和 Node.js 相关的例子。
假设你想要跟踪 Node.js 中所有当前被 rejected 的 Promises。
但是,你不想他们被垃圾回收,因为如果他们被垃圾回收了,这些 Promises 的引用就会丢失。
然而,如果保留对 Promises 的引用,则会导致内存泄漏,因为不会发生垃圾回收。
现在看来,无论如何你都需要保留每个被 rejected 的引用,但是我们可以使用 WeakMap ,既可以拿到每个 Promise 的引用,又可以使其被垃圾回收。
WeakMap 是怎么做到的?
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。
这意味着,我们无法对其进行枚举并且获得其 values。
但是 WeakMap 中,我们可以基于键存储数据,当该键被垃圾回收时,值也会被垃圾回收。
这意味着,你可以保持 Promise 的状态,并且该对象仍然可以被垃圾回收。
以后,如果你得到一个对象的引用,你可以检查你是否有任何与之相关的状态并报告它。
下面是Petka Antonov用来实现 未经处理的Promise钩子如下所示:
process.on('unhandledRejection', function(reason, p) {
console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason);
// 进行其他的处理
});
我们在 WeakMap 中保存了相关 Promise 的信息,并且可以知道何时处理了被 rejected 的 promise。
其他使用场景
一些适合使用 WeakMap 防止内存泄漏的场景包括:
- 保留关于特定对象的私有数据,并且只将对该对象的访问权限授予Map的引用者。
- 保存有关对象的数据而不更改它们或产生开销。
- 在浏览器中保存有关宿主对象(如DOM节点)的数据。
- 从外部向对象添加功能。
总结
Map的键可以是任意类型,WeakMap只接受对象作为键(null除外),不接受其他类型的值作为键Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键;WeakMap的键是弱引用,键所指向的对象可以被垃圾回收,此时键是无效的Map可以被遍历,WeakMap不能被遍历(WeakMap 中,每个键对自己所引用对象的引用都是弱引用,在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 的 key 是不可枚举的。)
方法:
- has(key):判断是否有 key 关联对象
- get(key):返回key关联对象(没有则则返回 undefined)
- set(key):设置一组key关联对象
- delete(key):移除 key 的关联对
补充
1、注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
2、除了 WeakMap 还有 WeakSet 都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏