Home>Article>Web Front-end> What is WeakMap? Why is it used as a cache area in Vue3 responsive source code?

What is WeakMap? Why is it used as a cache area in Vue3 responsive source code?

青灯夜游
青灯夜游 forward
2021-12-22 10:19:35 2490browse

This article will take you to understand WeakMap in Vue3 and introduce why WeakMap is used as the "cache area" in Vue 3 responsive source code. I hope it will be helpful to everyone.

What is WeakMap? Why is it used as a cache area in Vue3 responsive source code?

[Related recommendation: "vue.js Tutorial"]

Watch while reading some code of Vue 3 responsive principle When it is performing responsive processing, a "cache area" is created for each object usingWeakMap. The code is as follows:

// 注意下面这句代码! const reactiveMap = new WeakMap(); // 核心进行劫持的方法 处理 get 和 set 的逻辑 const mutableHandlers = { get, set } function reactive(target: object) { return createReactiveObject(target, mutableHandlers, reactiveMap); } /** * @description 创建响应式对象 * @param {Object} target 需要被代理的目标对象 * @param {Function} baseHandlers 针对每种方式对应的不同处理函数 * @param {Object} proxyMap WeakMap 对象 */ function createReactiveObject(target, baseHandlers, proxyMap) { // 检测 target 是不是对象,不是对象直接返回,不进行代理 if (!isObject(target)) { return target } const existsProxy = proxyMap.get(target); // 如果该对象已经被代理过了,则直接返回,不进行重复代理 if (existsProxy) { return existsProxy } // 未被代理过,则创建代理对象 const proxy = new Proxy(target,baseHandlers); // 缓存,避免重复代理,即避免 reactive(reactive(Object)) 的情况出现 proxyMap.set(target,proxy); return proxy }

As can be seen from the above code,WeakMapThe purpose of the cache area is to prevent objects from being repeatedly proxied.

Why does Vue 3 useWeakMapto cache proxy objects? Why not use other methods for caching, such asMap?

What is WeakMap

WeakMapThe object is a collection of key-value pairs, where the keys areweak references. The keys must beobjects, and the values can be arbitrary.

Syntax

new WeakMap([iterable])

Iterableis an array (two-element array) or other iterable object whose elements are key-value pairs. Each key-value pair will be added to a newWeakMap.

Method

WeakMapThere are four methods:get,set,has,delete, let’s take a look at its general usage:

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); // value 可以是任意值,包括一个对象或一个函数 wm2.set(o3, undefined); wm2.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个 WeakMap 对象 wm1.get(o2); // "azerty" wm2.get(o2); // undefined,wm2 中没有 o2 这个键 wm2.get(o3); // undefined,值就是 undefined wm1.has(o2); // true wm2.has(o2); // false wm2.has(o3); // true (即使值是 undefined) wm3.set(o1, 37); wm3.get(o1); // 37 wm1.has(o1); // true wm1.delete(o1); wm1.has(o1); // false

Why useWeakMapinstead ofMap

In JavaScript, themapAPI can be implemented through four API methods sharing two arrays (one for keys and one for values). In this way, when setting a value to thismap, the key and value will be added to the end of both arrays at the same time. This makes the key and value indexes correspond in the two arrays. When getting a value from thismap, you need to iterate through all the keys and then use the index to retrieve the corresponding value from the array where the value is stored.

But such an implementation will have two big shortcomings. First, the assignment and search operations have a time complexity ofO(n)(nis the key number of value pairs), because both operations require traversing the entire array to match.

Another disadvantage is that it may causememory leak, because the array will always reference each key and value. Such references prevent thegarbage collection algorithm from recycling them, even if no other references exist.

let jser = { name: "dachui" }; let array = [ jser ]; jser = null; // 覆盖引用

In the above code, we put an object into the array, then as long as the array exists, then the object will also exist,even if there are no other references to the object.

let jser = { name: "dachui" }; let map = new Map(); map.set(jser, ""); jser = null; // 覆盖引用

Similarly, if we use an object as a key for a regularMap, then the object will exist when theMapexists. It takes up memory and will not be reclaimed by the garbage collection mechanism.

In contrast, the nativeWeakMapholds aweak referencefor each key object, which means that garbage collection can Do it correctly.

It is precisely because of such weak references thatkeyofWeakMapis not enumerable (no method can give allkey) . Ifkeyis enumerable, its list will be affected by the garbage collection mechanism, resulting in undefined results. Therefore, if you want a list ofkeyvalues for this type of object, you should use aMap.

To sum up, we can draw the following conclusion:The object pointed to by the key of WeakMap is not counted in the garbage collection mechanism.

So, if you want to add data to the object and don't want to interfere with the garbage collection mechanism, you can useWeakMap.

Everyone should know after seeing this, the reason why Vue 3 usesWeakMapas a buffer is to correctly garbage collectthe data that is no longer used.

What is a weak reference

Regarding "weak reference", Wikipedia gives the answer:

In computer programming, Weak referenceand Strong ReferenceRelative, refers to a reference that cannot ensure that the object it refers to will not be recycled by the garbage collector. An object is considered inaccessible (or weakly accessible) if it is only referenced by weak references, and therefore may be recycledat any time.

Why do weak references appear?

So, why do weak references appear? In addition to solving the above problems, what other problems can weak references solve? To answer these questions, we first need to understand how theV8engine performs garbage collection.

ForJSer, memory management is automatic and invisible, all thanks to theV8engine silently helping us find the files we don’t need to use behind the scenes. memory and clean it up.

So, what happens when we no longer need something,V8How does the engine find it and clean it up?

现在各大浏览器通常用采用的垃圾回收有两种方法,一种是「引用计数」,另外一种就是「标记清除」。下面我们来看一下:

标记清除

标记清除被称为mark-and-sweep,它是基于可达性来判断对象是否存活的,它会定期执行以下「垃圾回收」步骤:

  • 垃圾收集器找到所有的根,并标记(记住)它们。

  • 然后它遍历并标记来自它们的所有引用。所有被遍历到的对象都会被记住,以免将来再次遍历到同一个对象。

  • ……如此操作,直到所有可达的(从根部)引用都被访问到。

  • 没有被标记的对象都会被删除。

我们还可以将这个过程想象成从根溢出一个巨大的油漆桶,它流经所有引用并标记所有可到达的对象,然后移除未标记的。

引用计数

引用计数方式最基本的形态就是让每个被管理的对象与一个引用计数器关联在一起,该计数器记录着该对象当前被引用的次数,每当创建一个新的引用指向该对象时其计数器就加 1,每当指向该对象的引用失效时计数器就减 1。当该计数器的值降到 0 就认为对象死亡。

区别

引用计数与基于「可达性」的标记清除的内存管理方式最大的区别就是,前者只需要局部的信息,而后者需要全局的信息

在引用计数中每个计数器只记录了其对应对象的局部信息 —— 被引用的次数,而没有(也不需要)一份全局的对象图的生死信息。

由于只维护局部信息,所以不需要扫描全局对象图就可以识别并释放死对象。但也因为缺乏全局对象图信息,所以无法处理循环引用的状况。

所以,更高级的引用计数实现会引入弱引用的概念来打破某些已知的循环引用。

WeakMap 应用

存储 DOM 节点

WeakMap应用的典型场合就是以DOM节点作为键名。下面是一个例子。

const myWeakmap = newWeakMap(); myWeakmap.set( document.getElementById('logo'), { timesClicked: 0 }, ); document.getElementById('logo').addEventListener('click', () => { const logoData = myWeakmap.get(document.getElementById('logo')); logoData.timesClicked++; }, false);

上面代码中,document.getElementById('logo')是一个DOM节点,每当发生click事件,就更新一下状态。我们将这个状态作为值放在WeakMap里,对应的键就是这个节点对象。一旦这个DOM节点删除,该状态就会自动消失,不存在内存泄漏风险。

数据缓存

谜底就在谜面上,文章一开头我们提出的问题就是这里的答案。Vue 3 在实现响应式原理的时候就是使用了WeakMap来作为响应式对象的「缓存区」。

关于这一点用法也很简单,当我们需要关联对象和数据,比如在不修改原有对象的情况下储存某些属性或者根据对象储存一些计算的值等,而又不想手动去管理这些内存问题的时候就可以使用WeakMap

部署类中的私有属性

WeakMap的另一个用处是部署类中的私有属性。

值得一提的是,TypeScript 中已经实现的private私有属性原理就是利用WeakMap

私有属性应该是不能被外界访问到,不能被多个实例共享,JavaScript 中约定俗成地使用下划线来标记私有属性和方法,一定程度来说是不靠谱的。

下面我们用三种方法来实现:

  • 版本一:闭包
const testFn = (function () { let data; class Test { constructor(val) { data = val } getData() { return data; } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 4 console.log(test2.getData()); // 4

可以看到最后都输出4,多实例共享私有属性了,所以版本一不符合。

  • 版本二:Symbol
const testFn = (function () { let data = Symbol('data') class Test { constructor(val) { this[data] = val } getData() { return this[data] } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 3 console.log(test2.getData()); // 4 console.log(test1[Object.getOwnPropertySymbols(test1)[0]]); // 3 console.log(test2[Object.getOwnPropertySymbols(test2)[0]]); // 4

使用Symbol虽然实现了而且正确输出了34,但是我们发现可以在外界不通过getData方法直接拿到私有属性,所以这种方法也不满足我们的要求。

  • 版本三:WeakMap
const testFn = (function () { let data = new WeakMap() class Test { constructor(val) { data.set(this, val) } getData() { return data.get(this) } } return Test; })(); let test1 = new testFn(3); let test2 = new testFn(4); console.log(test1.getData()); // 3 console.log(test2.getData()); // 4

如上,完美解决~~

更多编程相关知识,请访问:编程入门!!

The above is the detailed content of What is WeakMap? Why is it used as a cache area in Vue3 responsive source code?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete