你知道 WeakMap 吗

发布于:2023-01-16 ⋅ 阅读:(991) ⋅ 点赞:(0)

什么是WeakMap类型

WeakMap)是 ECMAScript 6 新增 的 一种 “弱映射” 集合类型,它这门语言带来了增强的键/值对存储机制

WeakMap 中的“weak”(弱),描述的是 JavaScript 垃圾回收程序对待“弱映射”中键的方式;它的键被弱保持,也就是说,当其键所指对象没有其他地方引用的时候,它会被 GC ( Garbage Collection 垃圾回收) 回收掉

基本使用

WeakMap 的键必须是Object类型或继承Object的类型(尝试使用非对象设置键会抛出TypeError),值可以是任意类型;

初始化空映射

 const wm = new WeakMap()
 // wm : WeakMap {}

初始化填充弱映射,构造函数可以接收一个包含键/值对数组的可迭代对象

 const key1 = { 'id1': 11 }
 const key2 = { 'id2': 22 }
 const wm2 = new WeakMap([
     [key1, "value1"],
     [key2, "value2"]
 ])
 console.log(wm); // WeakMap {{Object => "value1"}, {Object => "value2"}}
 console.log(wm2.get(key1)); // value2

注意:初始化是全有或全无的操作,只要有一个键无效就会抛出错误,导致整个初始化失败!

与 Map 一样,set() 方法返回弱映射实例,因此可以把多个操作连缀起来,包括初始化声明

 const wm = new WeakMap().set('key1', 111).set('key2', [222])
 // wm : WeakMap {{key2: 222} => ['two'], {key1: 111} => 'one'}

以此可见,使用 set() 多次存值时,排列循序为从后到前

上面用到了根据指定键获取值的 .get 方法API与添加或更新指定键和值的键值对 .set()方法API,由于WeakMapMap 的“兄弟”类型,其 API 也是 Map 的子集,所以API使用方法与Map一致;相信大家对于Map()肯定熟悉不少,如果还不了解如何使用的同学可以前往我上一篇文章 你知道JavaScript中的Map类型吗 进行翻阅学习;

内置方法

WeakMap 与 Map 在 API 上的区别主要是两个:

  1. 没有遍历操作(即没有keys()values()entries()方法),也没有size属性;因为没有办法列出所有键名,某个键名是否存在完全不可预测,跟垃圾回收机制是否运行相关;这一秒可以取到键名,下一秒垃圾回收机制突然运行了,这个键名就没了 ( ̄▽ ̄)",所以为了防止出现不确定性,就统一规定不能取到键名
  2. 无法清空:即不支持clear方法

因此,WeakMap只有四个方法可用:get()set()has()delete()

 

关于弱映射对待垃圾回收

举个栗子

例如我们想要为e1元素添加文字说明,就形成了 arre1的引用;一旦不再需要这个对象,我们就必须手动删除这个引用,否则垃圾回收机制就不会释放e1所占用的内存

 const e1 = document.getElementById('btn');
 // 形成引用
 const arr = [ e1 , '点击Button'];// 当不再需要时只能手动设置null使其解除引用
 arr = null

这样释放引用的写法显然很不方便;一旦忘了写,就会造成内存泄露

WeakMap 就是为了解决这个问题而诞生的,它的键名所引用的对象都是弱引用,只要键存在,键/值对就会存在于映射中,并被当作对值的引用,因此就不会被当作垃圾回收;所以只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存;

一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用

我们把上面的场景通过使用 WeakMap 来解决一下:

 const wm = new WeakMap();
 const e1 = document.getElementById('btn');
 // 使用 .set 建立弱引用连接
 wm.set(e1,"弱引用")
 wm.get(e1) // "弱引用"

这时 WeakMap 里面对e1的引用就是弱引用,不会被计入垃圾回收机制 ;但当节点从 DOM 树中被删除后,垃圾回收程序就可以立即释放其内存(假设没有其他地方引用这个对象)

 

使用node检测内存问题

这里插入一个使用node检测内存的方法,这个方法是在阮一峰 ECMAScript 6 (ES6) 标准入门教程 第三版中学到的( ̄︶ ̄)↗ ;

语法:global.gc() 用于执行垃圾回收;process.memoryUsage() 查看内存占用状态

  • 首先打开Node命令行

     $ node --expose-gc
    
  • 执行垃圾回收并查看内存占用初始状态

     // 手动执行一次垃圾回收,保证获取的内存使用状态准确
     > global.gc();
     undefined// 查看内存占用的初始状态,heapUsed 为 4M 左右
     > process.memoryUsage();
     { rss: 21106688,
       heapTotal: 7376896,
       heapUsed: 4153936,
       external: 9059 }
    
  • 创建变量测试一下

     // 创建一个 WeakMap() 弱映射
     > let wm = new WeakMap();
     undefined// 新建一个变量 key,指向一个 5*1024*1024 的数组
     > let key = new Array(5 * 1024 * 1024);
     undefined// 设置 WeakMap 实例的键名,也指向 key 数组
     // 这时,key 数组实际被引用了两次,
     // 变量 key 引用一次,WeakMap 的键名引用了第二次
     // 但是,WeakMap 是弱引用,对于引擎来说,引用计数还是1
     > wm.set(key, 1);
     WeakMap {}
  • 再次垃圾回收查看内存占用

     // 执行垃圾回收
     > global.gc();
     undefined// 这时内存占用 heapUsed 增加到 45M 了
     > process.memoryUsage();
     { rss: 67538944,
       heapTotal: 7376896,
       heapUsed: 45782816,
       external: 8945 }// 清除变量 key 对数组的引用,
     // 但没有手动清除 WeakMap 实例的键名对数组的引用
     > key = null;
     null// 再次执行垃圾回收
     > global.gc();
     undefined// 内存占用 heapUsed 变回 4M 左右,
     // 可以看到 WeakMap 的键名引用没有阻止 gc 对内存的回收
     > process.memoryUsage();
     { rss: 20639744,
       heapTotal: 8425472,
       heapUsed: 3979792,
       external: 8956 }
    

最后如果本文对于本文有疑惑,还请指导勘正 (●’◡’●)

本文含有隐藏内容,请 开通VIP 后查看