小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
引用中强弱具体的含义是什么? 以及为什么要引入弱引用 WeakMap,以及弱引用 WeakMap 的应用
Map 在 JavaScript 中的实现
Map 在 JavaScript 中的实现是采用了 2 数组,一个用于保存 keys,而另一个用于保存 values,在 2 个数组间共享 Map 4 个 API,get、set、has 和 delete。这里拿 set 来说明内部实现的方式,当 set 会同时将一对 value 和 key 分别推到 2 个数组的末端,get 会遍历 keys 数组得到要查询键对应的索引,然后拿索引取 values 中取值。
这样会有两个问题
时间复杂度问题
时间复杂度是 ,其中 n 是 keys 数量,因为查找时会遍历 keys 进行搜索匹配的键值。
内存泄漏问题
对于强类型需要长期在内存中维护 2 个用于 keys 和 values,当其他对象引用清空,因为 Map 会阻止对象被垃圾回收掉,所以可能会造成内存泄漏。
什么又是弱引用
let wm1 = new WeakMap()
wm1.set("title","machine learning")
// Uncaught TypeError: Invalid value used as weak map key
在 WeakMap
的键的取值不支持基本数据类型,例如 String、Number 等基本数据类型,而支持对象
mw1.set({},"value")
为什么会有这样的设计呢,这里给大家稍作解释。例如我们都需要让数据与 DOM 相关联
const someThingDiv = document.querySelector("#box");
mw1.set({title:"machine learning"},someThingDiv)
可以通过将对象键作为数据,而 DOM 元素作为值,来通过 weakMap 来建立一种数据到 DOM 元素之间的关系。
在 weakMap 中使用一个对象作为键,并且没有其他对这个对象的引用,该对象将会被从内存中自动清除。
let m1 = new Map();
tut_ml = {title:"machine learning"}
tut_dl = {title:"deep learning"}
m1.set(tut_ml,"machine learning tutorial");
m1.set(tut_dl,"deep learning tutorial");
tut_ml = null;
console.log(m1.size) //2
对于强引用 Map
即使 tut_ml
设置为 null
,因为是强引用所以并没有不会从内存清除掉该对象。所以m1
输出为
let mw1 = new WeakMap();
tut_ml = {title:"machine learning"}
tut_dl = {title:"deep learning"}
mw1.set(tut_ml,"machine learning tutorial");
mw1.set(tut_dl,"deep learning tutorial");
tut_ml = null
console.log(mw1) //
而对于弱引用当你把键设置为 null
则在 mw1
中对应的 tut_mul
也被从垃圾回收机制给清除了。
应用场景
额外数据
WeakMap
的主要应用场景是 额外数据的存储。假如正在处理一个“属于”另一个文件的一个对象,也可能是第三方库,并想存储一些与之相关的数据,那么这些数据就应该与这个对象共存,这时候就 WeakMap
就派上用场。将这些数据放到 WeakMap
中,并使用该对象作为这些数据的键,那么当该对象被垃圾回收机制回收后,这些数据也会垃圾回收机制清除了。
这个例子是比较经典的 WeakMap 应用的场景,也就是统计来访用户的数量,用户对象作为键而统计用户访问的次数
let visitsCountWeakMap = WeakMap();
function countUser(user){
let count = visitsCountWeakMap.get(user) || 0;
visitsCountWeakMap.set(user, count + 1);
}
好处就是当当 user 设置为 null 该用户访问的记录也就自动从 visitsCountWeakMap
中清除了。
let tony = { name: "Tony" };
countUser(tony); // 统计用户访问次数
tony = null;
缓存数据
如果对于用 Map 做缓存<对于多次调用同一个对象,只需在第一次调用时计算出结果,之后的调用可以直从 cache
中获取。不过用 Map 的缺点是不再需要某个对象的时候,需要清理 cache
。如果用 WeakMap
替代 Map
,这个问题便会消失, 当对象被垃圾回收时,对应的缓存的结果也会被自动地从内存中清除。
let cache = new WeakMap();
// 计算并记结果
function process(obj) {
if (!cache.has(obj)) {
let result = obj;
cache.set(obj, result);
}
return cache.get(obj);
}
let tut = {title:'machine learing'};
let result1 = process(tut);
let result2 = process(tut);