preface

Let’s start with the features of WeakMap, and then talk about some application scenarios of WeakMap.

features

1. WeakMap accepts only objects as key names

const map = new WeakMap(a); map.set(1.2);
// TypeError: Invalid value used as weak map key
map.set(null.2);
// TypeError: Invalid value used as weak map key
Copy the code

2. The object referenced by the key name of a WeakMap is a weak reference

This sentence is very confusing to me. Personally, I think what this sentence really means is:

WeakMaps hold “weak” references to key objects,

WeakMaps holds a weak reference to the object referenced by the key name.

Let’s talk about weak quotes:

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.

In JavaScript, when we create an object, we create a strong reference:

var obj = new Object(a);Copy the code

It is only possible to reclaim objects referenced by obj if we manually set obj = null.

Whereas if we could create an object with a weak reference:

// Suppose you can create one like this
var obj = new WeakObject();
Copy the code

We don’t have to do anything but wait for the garbage collection mechanism to execute and the object referenced by obj will be collected.

Let’s look at this one again:

WeakMaps maintains a weak reference to the object referenced by the key name

Normally, here’s an example:

const key = new Array(5 * 1024 * 1024);
const arr = [
  [key, 1]].Copy the code

In this way, we essentially create a strong reference for arR to the object referenced by the key (we assume the real object is called Obj).

So when you set key = null, you just remove the key’s strong reference to Obj, but you don’t remove the arR’s strong reference to Obj, so Obj is still not recycled.

The Map type is similar:

let map = new Map(a);let key = new Array(5 * 1024 * 1024);

// Create a strong reference to the key referenced by the map
map.set(key, 1);
// Key = null does not cause the original reference object of the key to be reclaimed
key = null;
Copy the code

We can prove this by using Node:

// Allow manual garbage collection
node --expose-gc

global.gc();
// Return the memory usage of Nodejs in bytes
process.memoryUsage(); // heapUsed: 4640360 ≈ 4.4m

let map = new Map(a);let key = new Array(5 * 1024 * 1024);
map.set(key, 1);
global.gc();
process.memoryUsage(); // heapUsed: 46751472 Note that this is about 44.6m

key = null;
global.gc();
process.memoryUsage(); // heapUsed: 46754648 ≈ 44.6M

// This statement is useless because the key is already null
map.delete(key);
global.gc();
process.memoryUsage(); // heapUsed: 46755856 ≈ 44.6M
Copy the code

If you want Obj to be recycled, you need to delete(key) first and then key = null:

let map = new Map(a);let key = new Array(5 * 1024 * 1024);
map.set(key, 1);
map.delete(key);
key = null;
Copy the code

Let’s prove this again with Node:

node --expose-gc

global.gc();
process.memoryUsage(); // heapUsed: 4638376 ≈ 4.4m

let map = new Map(a);let key = new Array(5 * 1024 * 1024);
map.set(key, 1);
global.gc();
process.memoryUsage(); // heapUsed: 46727816 ≈ 44.6M

map.delete(key);
global.gc();
process.memoryUsage(); // heapUsed: 46748352 ≈ 44.6M

key = null;
global.gc();
process.memoryUsage(); // heapUsed: 4808064 ≈ 4.6m
Copy the code

This is where WeakMap comes in:

const wm = new WeakMap(a);let key = new Array(5 * 1024 * 1024);
wm.set(key, 1);
key = null;
Copy the code

When we set wm.set(key, 1), we create a weak wm reference to the object referenced by key, but since let key = new Array(5 * 1024 * 1024) creates a strong reference to the object referenced by key, The referenced object is not reclaimed, but when we set key = null, there is only a weak wm reference to the referenced object, which will be reclaimed the next time garbage collection is performed.

Let’s use Node to prove this:

node --expose-gc

global.gc();
process.memoryUsage(); // heapUsed: 4638992 ≈ 4.4m

const wm = new WeakMap(a);let key = new Array(5 * 1024 * 1024);
wm.set(key, 1);
global.gc();
process.memoryUsage(); // heapUsed: 46776176 ≈ 44.6M

key = null;
global.gc();
process.memoryUsage(); // heapUsed: 4800792 ≈ 4.6m
Copy the code

So WeakMap can help you save the step of manually deleting the object associated data, so when you can’t or don’t want to control the life cycle of associated data you can consider using WeakMap.

To summarize the nature of this weak reference, WeakMaps maintains a weak reference to the object referenced by the key name, that is, the garbage collection mechanism does not take the reference into account. Garbage collection frees the memory used by the referenced object as long as all other references to the object are cleared. In other words, once no longer needed, the key name object and the corresponding key value pair in the WeakMap will disappear automatically, without manually deleting the reference.

It is precisely because of such features, the number of members inside WeakMap depends on whether the garbage collection mechanism is running. It is likely that the number of members is different before and after the operation, and when the garbage collection mechanism is running is unpredictable, so ES6 stipulates that WeakMap cannot be traversal.

Therefore, WeakMap is not like Map. First, there is no traversal operation (that is, no keys(), values() and entries() methods), no size property, and no clear method is supported. Therefore, there are only four methods available for WeakMap: Get (), set(), has(), delete().

application

1. Save related data on the DOM object

JQuery uses an internal object to manage the DOM and its corresponding data. When you delete the DOM element, you can use the $.data() method to store information on the DOM object. When the DOM object is set to empty, the associated data will not be deleted. You must manually execute the $.removeData() method to delete the associated data. WeakMap can simplify this operation:

let wm = new WeakMap(), element = document.querySelector(".element");
wm.set(element, "data");

let value = wm.get(elemet);
console.log(value); // data

element.parentNode.removeChild(element);
element = null;
Copy the code

2. Data cache

From the previous example, we can also see that when we need to associate objects and data, such as storing some attributes or some calculated values according to the object without modifying the original object, and do not want to manage the death of these data is very suitable to consider using WeakMap. Data caching is a good example:

const cache = new WeakMap(a);function countOwnKeys(obj) {
    if (cache.has(obj)) {
        console.log('Cached');
        return cache.get(obj);
    } else {
        console.log('Computed');
        const count = Object.keys(obj).length;
        cache.set(obj, count);
        returncount; }}Copy the code

3. Private properties

WeakMap can also be used to implement private variables, but there are many ways to implement private variables in ES6, and this is just one of them:

const privateData = new WeakMap(a);class Person {
    constructor(name, age) {
        privateData.set(this, { name: name, age: age });
    }

    getName() {
        return privateData.get(this).name;
    }

    getAge() {
        return privateData.get(this).age; }}export default Person;
Copy the code

ES6 series

ES6 series directory address: github.com/mqyqingfeng…

ES6 series is expected to write about 20 articles, aiming to improve the understanding of ES6 part of the knowledge, focusing on block-level scope, tag template, arrow function, Symbol, Set, Map and Promise simulation implementation, module loading scheme, asynchronous processing and other content.

If there are any mistakes or irregularities, please be sure to correct them. Thank you very much. If you like it or have some inspiration, welcome star, which is also a kind of encouragement to the author.