标签:就是 tor 垃圾回收 doc asc result 今天 https cached
而且JavaScript既然已经有了Map类型的数据结构,为什么还有一种叫做WeakMap类型的数据结构呢?它和垃圾回收有什么关系?
WeakMap很早之前就遇到过,但是没有系统学习过,今天就来对它一探究竟。
WeakMap对象是一组键值对的集合,其中key是弱引用的
WeakMap的key必须是对象类型,value可以是任意类型
弱引用的意义:如果是作为key的对象没有任何地方引用它的话,垃圾收集器(GC)会将其标记为目标并且进行垃圾回收。
key:必须是任意object类型(对象、数组、Map、WeakMap等等)
value:any(任意类型,所以也包括undefined,null)
WeakMap的key是不可枚举的,而Map是可枚举的。
不可枚举就意味着获取不到WeakMap的key列表。
设计为不可枚举的原因是因为:如果枚举WeakMap的key,那就需要依赖垃圾回收器(GC)的状态,从而引入不确定性。
map API在js中可以通过共享4个API(get,set,has,delete)的两个数组来实现:一个存储key,一个存储value。在这个map上设置元素同步推入一个key和一个value到数组尾部。作为结果,key和value的索引会和两个数组绑定起来。从map获取一个值得话,会遍历所有key去找到一个匹配的,然后使用这个匹配到的index从values数组中查询到对应的值。
这样实现的话会有2个主要的弊端:
首先是set和search的时间复杂度是O(n),n是map中key数组的数量,因为都需要遍历列表去查找到需要的值
其次是会造成内存泄漏,因为数组需要无期限地去确保每个key和每个value的引用。这些引用会导致阻止key被垃圾回收掉,即使这个对象没有任何地方再引用到了,key对应的value也同样会被阻止垃圾回收。
相比之下,原生的WeakMap会保持对key的“弱”引用。原生的WeakMap不会阻止垃圾回收,最终会移除对key对象的引用。“弱”引用同样可以让value很好地垃圾回收。WeakMap特别适用于key映射的信息只有不被垃圾回收时才有价值的场景,换句话说就是WeakMap适用于动态垃圾回收key的场景。
因为引用是弱的,所以WeakMap的键是不能枚举的。没有方法去获取key的列表。如果枚举WeakMap的key,那就需要依赖垃圾回收器(GC)的状态,从而引入不确定性。如果必须要有key的话,应该去使用Map。
new WeakMap()
new WeakMap(iterable)
其中iterable是数组或者任意可以迭代的对象,需要拥有key-value对(一般是一个二维数组)。null会被当做undefined。
const iterable = [
[{foo:1}, 1],
[[1,2,3], 2],
[window, 3]
]
const iwm = new WeakMap(iterable)
// WeakMap {{…} => 1, Window => 3, Array(3) => 2}
删除key关联的任意值。删除后WeakMap.prototype.has(key)返回false。
返回与key关联的值,假设不存在关联值得话返回undefined。
返回key在WeakMap上是否存在的结果。
在WeakMap对象上为对应key设置指定的value。并且返回WeakMap对象
const wm1 = new WeakMap(),
wm2 = new WeakMap(),
wm3 = new WeakMap();
const o1 = {},
o2 = function() {},
o3 = window;
wm1.set(o1, 37);
wm1.set(o2, ‘azerty‘);
wm2.set(o1, o2); // WeakMap的值可以是任意类型,包括object和function
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // key和value可以是任意对象。包括WeakMap!
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined, wm2上没有o2这个key
wm2.get(o3); // undefined, 因为这是设置的值
wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即使value是undefined)
wm3.set(o1, 37);
wm3.get(o1); // 37
wm1.has(o1); // true
wm1.delete(o1);
wm1.has(o1); // false
实例和原型链上的数据和方法是公开的,所以可以通过WeakMap类型的私有变量去隐藏实现细节。
const privates = new WeakMap();
function Public() {
const me = {
// Private data goes here
};
privates.set(this, me);
}
Public.prototype.method = function () {
const me = privates.get(this);
// Do stuff with private data in `me`...
};
module.exports = Public;
class ClearableWeakMap {
constructor(init) {
this._wm = new WeakMap(init);
}
clear() {
this._wm = new WeakMap();
}
delete(k) {
return this._wm.delete(k);
}
get(k) {
return this._wm.get(k);
}
has(k) {
return this._wm.has(k);
}
set(k, v) {
this._wm.set(k, v);
return this;
}
}
const key1 = {foo:1};
const key2 = [1,2,3];
const key3 = window;
const cwm = new ClearableWeakMap([
[key1, 1],
[key2, 2],
[key3, 3]
])
cwm.has(key1) // true
console.log(cwm);// ClearableWeakMap {_wm: WeakMap {Window => 3, {…} => 1, Array(3) => 2}}
cwm.clear(); // 垃圾回收当前WeakMap,并且声称新的空WeakMap
cwm.has(key1) // false
console.log(cwm);// ClearableWeakMap {_wm: WeakMap {}}
https://www.98891.com/article-53-1.html
实现缓存函数的方式有很多种,比如单次缓存,Map式全量缓存,LRU最近最少缓存等等。
那么为什么还需要WeakMap式的缓存函数呢?这是因为入参为对象类型的缓存且方便浏览器垃圾回收。
function memoizeWeakMap(fn) {
const wm = new WeakMap();
return function (arg) {
if (wm.has(arg)) {
return wm.get(arg);
}
const cachedArg = arg;
const cachedResult = fn(arg);
wm.set(cachedArg, cachedResult)
console.log(‘weakmap object‘, wm)
return cachedResult;
};
}
let testFn = (bar) => {return Object.prototype.toString.call(bar)}; // 这里需要改造一下,改造完返回传入对象的类型
let memoizeWeakMapFn = memoizeWeakMap(testFn);
memoizeWeakMapFn(document) // weakmap对document生成缓存
memoizeWeakMapFn([1,2,3]) // weakmap对[1,2,3]生成缓存
memoizeWeakMapFn(function(){}) // weakmap对function(){}生成缓存
memoizeWeakMapFn(new WeakMap()) // weakmap对WeakMap实例生成缓存
memoizeWeakMapFn(new Map()) // weakmap对Map实例生成缓存
memoizeWeakMapFn(new Set()) // weakmap对Set实例生成缓存
WeakMap:
0: {Array(3) => "[object Array]"}
1: {function(){} => "[object Function]"}
2: {WeakMap => "[object WeakMap]"}
3: {Map(0) => "[object Map]"}
4: {#document => "[object htmlDocument]"}
5: {Set(0) => "[object Set]"}
// 忽略部分代码同上
setTimeout(()=>{
memoizeWeakMapFn(document)
},5000)
此时有时最后一次weakmap的打印结果如下:
WeakMap:
0: {#document => "[object htmlDocument]"}
因为打印时垃圾回收可能并没有执行完成,虽然会带来不确定性,但是可以确定的是,假设对象没有再被引用,WeakMap中的key会被浏览器自动垃圾回收掉。
这是因为[1,2,3], function(){},new WeakMap(),new Map(),new Set()在后面都没有再继续引用了,而且因为它们作为了WeakMap的key,所以会被浏览器自动垃圾回收掉。
保持一个变量对它的引用。
let memoizeWeakMapFn = memoizeWeakMap(testFn);
let retainArray = [1,2,3]; // 保持引用避免被垃圾回收
let retainMap = new Map(); // 保持引用避免被垃圾回收
memoizeWeakMapFn(document) // weakmap对document生成缓存
memoizeWeakMapFn(retainArray) // weakmap对[1,2,3]生成缓存
memoizeWeakMapFn(function(){}) // weakmap对function(){}生成缓存
memoizeWeakMapFn(new WeakMap()) // weakmap对WeakMap实例生成缓存
memoizeWeakMapFn(retainMap) // weakmap对Map实例生成缓存
memoizeWeakMapFn(new Set()) // weakmap对Set实例生成缓存
setTimeout(()=>{
memoizeWeakMapFn(document)
},5000)
此时打印结果为:
WeakMap:
0: {#document => "[object HTMLDocument]"}
1: {Map(0) => "[object Map]"}
2: {Array(3) => "[object Array]"}
这是因为[1,2,3], new Map()被变量retainArray和retainMap持续引用着,所以不会被垃圾回收。而function(){},new WeakMap(),new Set()都没有再继续引用了,而且因为它们作为了WeakMap的key,所以会被浏览器自动垃圾回收掉。
可以借助Chrome DevTools的memory面板工具,有一个手动触发垃圾回收的按钮。
// ...
setTimeout(()=>{
memoizeWeakMapFn(document)
},5000)
比如在上面的例子中,设置了一个5秒的延时:只要代码运行后的5秒内,去手动触发“垃圾回收按钮”,就可以很精确地看到WeakMap的key被垃圾回收了。
当然5秒这个时间是可以人为调整的,保证自己能在setTimeout内的代码运行前触发对WeakMap的垃圾回收即可,可以适当调大。
标签:就是 tor 垃圾回收 doc asc result 今天 https cached
原文地址:https://www.cnblogs.com/Qooo/p/14933374.html