概念
在看vue源码的时候发现里面用到了WeakMap,这个东西之前没见过,不太了解,所以这里学习并记录一下。
- 先看一下mdn上面的介绍,WeakMap 是一种键值对的集合,其中的键必须是对象或非全局注册的符号,且值可以是任意的 JavaScript 类型,并且不会创建对它的键的强引用。换句话说,一个对象作为 WeakMap 的键存在,不会阻止该对象被垃圾回收。一旦一个对象作为键被回收,那么在 WeakMap 中相应的值便成为了进行垃圾回收的候选对象,只要它们没有其他的引用存在。唯一可以作为 WeakMap 的键的原始类型是非全局注册的符号,因为非全局注册的符号是保证唯一的,并且不能被重新创建。
翻译一下就是WeakMap可以像Map一样存储数据,但是区别也很明显
- key必须是对象或者Smybol类型
- value值在除了自己以外,没有其他的引用时,这个value值可以被垃圾回收
验证
- WeakMap示例
const weakMap = new WeakMap()
function fn() {
for (let i = 0; i < 10000; i++) {
const obj = createBigMemData(10000) //创建obj,为引用类型
const symbolI = Symbol(i);
weakMap.set(symbolI, obj)
}
}
// 创建一个大内存数据
function createBigMemData(num) {
return new Array(num).fill(1)
}
fn()
console.log("-----end------")
执行完上面的代码,在浏览器中查看内存,截图如下:
可见,所有通过createBigMemData生成的obj都被释放掉了。但是没有对比不好直接这么推测,所以搞个其他示例(没有释放掉的)做个对比。
- Map示例对比
const map = new Map();
function fn() {
for (let i = 0; i < 1000; i++) {
const obj = createBigMemData(10000) //创建obj,为引用类型
const symbolI = Symbol(i);
map.set(symbolI, obj)
}
}
console.log("-----end------")
function createBigMemData(num) {
return new Array(num).fill(1)
}
fn();
执行完后,继续查看内存,截图如下:
由此可见,后面Map存储的obj对象指向的内存是没有释放的。
那么有的小伙伴会问,如何才能把这个内存释放掉呢?这个也是大家在平时开发过程中容易遇到的问题。
释放内存代码如下:
let map = new Map();
function fn() {
for (let i = 0; i < 1000; i++) {
const obj = createBigMemData(10000) //创建obj,为引用类型
const symbolI = Symbol(i);
map.set(symbolI, obj)
}
}
console.log("-----end------")
function createBigMemData(num) {
return new Array(num).fill(1)
}
fn();
function releaseMem() {
map = null;
//or
// map.clear();
}
releaseMem();
执行完后,继续查看内存,截图如下:
那么我们只需要将引用obj对象的Map对象进行清空或者置为null就可以将指向obj的引用干掉,那么obj就会被浏览器及时回收了。
- 模仿vue源码写个WeakMap内存释放的过程
let weakMap = new WeakMap()
let otherMap = new Map()
function fn() {
for (let i = 0; i < 1000; i++) {
const obj = createBigMemData(10000) //创建obj,为引用类型
const symbolI = Symbol(i)
weakMap.set(symbolI, obj)
otherMap.set(symbolI, obj)
}
}
console.log("-----end------")
function createBigMemData(num) {
return new Array(num).fill(1)
}
fn()
// 十秒后清理obj的引用,回收内存
function releaseMem() {
setTimeout(() => {
otherMap = null;
console.log("-----release end------")
}, 10000);
}
releaseMem();
上面的代码刚开始运行完内存占用为40多M,但是等待10s左右后,将指向obj的otherMap引用清理掉后,内存占用就回到1.5M左右。
由此可见WeakMap指向obj的应用并没有阻止obj内存的回收,这也符合一开头的概念定义。Vue正是借助这一点,巧妙的实现了对象和代理对象的存储关系,并且在代理对象销毁的时候,不会因为WeakMap仍然持有对这个代理对象的引用而阻止内存对代理对象的回收。
完结撒花~