什么是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,由于WeakMap
是 Map
的“兄弟”类型,其 API 也是 Map
的子集,所以API使用方法与Map一致;相信大家对于Map()
肯定熟悉不少,如果还不了解如何使用的同学可以前往我上一篇文章 你知道JavaScript中的Map类型吗 进行翻阅学习;
内置方法
WeakMap 与 Map 在 API 上的区别主要是两个:
- 没有遍历操作(即没有
keys()
、values()
和entries()
方法),也没有size
属性;因为没有办法列出所有键名,某个键名是否存在完全不可预测,跟垃圾回收机制是否运行相关;这一秒可以取到键名,下一秒垃圾回收机制突然运行了,这个键名就没了 ( ̄▽ ̄)",所以为了防止出现不确定性,就统一规定不能取到键名 - 无法清空:即不支持
clear
方法
因此,WeakMap
只有四个方法可用:get()
、set()
、has()
、delete()
关于弱映射对待垃圾回收
举个栗子
例如我们想要为e1元素添加文字说明,就形成了 arr
对 e1
的引用;一旦不再需要这个对象,我们就必须手动删除这个引用,否则垃圾回收机制就不会释放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 }
最后如果本文对于本文有疑惑,还请指导勘正 (●’◡’●)