This is the seventh day of my participation in the August More text Challenge. For details, see:August is more challenging

preface

WeakMap is a new data structure introduced by ES6. Like Map, it is a mapping used to store key values.

Relevant methods

The method name and usage rules are the same as Map, but the traversal method of key value is missing, because the key value of WeakMap is not enumerable and may be GC at any time.

  • get(key)
  • set(key, value)
  • delete(key)
  • has(key)

A weak reference

In computer programming, a weak reference, as opposed to a strong reference, is a reference that does not guarantee that the object it references will not be reclaimed by the garbage collector. An object that is referenced only by weak references is considered inaccessible (or weakly accessible) and may therefore be reclaimed at any time. from Wiki

In general, a weak reference is also a pointer in nature, but unlike a strong reference, it works better with the garbage collector because it doesn’t hog the reference count.

Map vs WeakMap

There are three important differences between WeakMap and Map:

  • The key must be an object
  • The key is a weak reference object
  • WeakMapIs a black box, so you can’t iterate over it and get its size.

The browser console is a REPL environment. When you print a WeakMap instance, you can still get its contents. However, the Node console is also a REPL environment, but it cannot output the contents of the WeakMap instance.

Therefore, if you want an enumerable, cacheable key-value data structure for objects, you should use a Map. WeakMap is used if you want to add data to an object and don’t want to interfere with the garbage collection mechanism.

Usage scenarios

Many places where Object is used to implement key value storage can be replaced by WeakMap to achieve better performance.

Referencing DOM elements

When we need to create a mapping for some DOM variables, we can use Map, but not Object, because Object’s key is a string type and is implicitly cast.

Here’s an example:

Store a button object instance in the Map as a key and take the number of clicks as its value.

let btn1 = document.getElementById("btn");
const map = new Map(a); map.set(btn1,0);
btn1.addEventListener('click'.() = > map.set(btn1, map.get(btn1) + 1));Copy the code

Once we no longer need it, we remove it from the DOM, but the Map still holds a reference to the button object instance.

Even though the variable btn1 no longer points to the button object instance, since Map still holds a reference to the instance, it has a reference count of 1, which causes a memory leak because the instance cannot be garbage collected unless we manually dereferencing it.

Using WeakMap instead of Map can achieve the same function without affecting the normal garbage collection.

let btn1 = document.getElementById("btn");
const map = new WeakMap(a); map.set(btn1,0);
btn1.addEventListener('click'.() = > map.set(btn1, map.get(btn1) + 1));Copy the code

WeakMap holds a weak reference to the button object instance and does not occupy its reference count, so when the button is removed from the DOM or is not referenced by any variable, it is GC away.

Implement object caching

Weak references can also be used to implement caching. For example, a weak hash table is used, that is, a hash table that caches various reference objects by weak references. When the garbage collector runs, if the application’s memory usage is high enough, cached objects that are no longer referenced by other objects are automatically freed.

const cache = new WeakMap(a);// basket is an array
function computeTotalPrice(basket) {
    if (cache.has(basket)) {
        return cache.get(basket);
    } else {
        let total = 0;
        basket.forEach(itemPrice= > total += itemPrice)
        cache.set(basket, total)
        returntotal; }}Copy the code

Check for circular references to objects

This is also a form of object caching. The following is an object deep clone method chestnut, just a simple implementation.

const isObject = (target) = > (typeof target === "object" || typeof target === "function") && target ! = =null;

function deepClone(target, map = new WeakMap(a)) {
    if (map.get(target)) {
        return target;
    }
    // Get the constructor for the current value: get its type
    let constructor = target.constructor;
    // Check whether the current object target matches the regular and date format objects
    if (/^(RegExp|Date)$/i.test(constructor.name)) {
        // Create a new instance of a special object (regular class/date class)
        return new constructor(target);  
    }
    if (isObject(target)) {
        map.set(target, true);  // Mark the object referenced by the loop
        const cloneTarget = Array.isArray(target) ? [] : {};
        for (let prop in target) {
            if(target.hasOwnProperty(prop)) { cloneTarget[prop] = deepClone(target[prop], map); }}return cloneTarget;
    } else {
        returntarget; }}Copy the code

Implement the private variables of the class

At present, the familiar and methods of js classes are all public, but tc39 has a draft of class private variables: TC39 /proposal-class-fields, which may become the standard in the future.

Now, we can use the feature that WeakMap is a black box to implement class private variables:

let _data = new WeakMap(a);class Registry {
    constructor(person, action) {
        const data = []
        _data.set(this, data);
    }
    get() {
	return _data.get(this)}set(person, action){
        const data = _data.get(this)
        data.push([person,action])
        _data.set(this, data); }}Copy the code

Not only are WeakMap features taken advantage of, but closures are also taken advantage of.

conclusion

In summary, we can better optimize the memory usage of the program and implement some hack functions by using the feature that WeakMap’s keys are weak references and cannot be enumerated.