Writing is not easy, without the permission of the author is prohibited in any form of reprint!


If you think this article is good, welcome to follow, thumb up and share!


Continue to share technical blog posts, pay attention to WeChat public number 👉 front-end Leon


The Denver nuggets in the original

WeakMap

Front knowledge
JavaScript GC garbage collection mechanism

What is a WeakMap?

WeakMap is a combination of Key/Value, Key only accepts objects, does not accept basic types, and Value can be of any type.

methods

  • set(key, value)

Set a group of associated objects in WeakMap and return the WeakMap object

  • get(key)

Returns the associated object of key, or undefined if it does not exist

  • has(key)

Put back a Boolean value depending on whether there is a Key associated object

  • delete(key)

Remove the object associated with the key, and then execute the has(key) method that returns false

What’s the difference with MAP?

  1. The key/value of a Map can be of any type
  • The Key of WeakMap can only be an object, and the Value can be of any type
const name = "LeBron";
const person = {
  name: "LeBron",
  age: 21,
};

let wk = new WeakMap();

wk.set(person, "nice");
console.log(wk.get(person)); // nice
wk.set(name, 1); // TypeError: Invalid value used as weak map key

let map = new Map();
map.set(name, "JS");
map.set(person, "nice");
console.log(map.get(name)); // JS
console.log(map.get(person)); // nice
  1. The key/value of the Map is traversable because its keys/values are stored in an array.
  • WeakMap does not have an array storing key/value, so it cannot be traversed.
const name = "LeBron";
const person = {
  name: "LeBron",
  age: 21,
};

let wk = new WeakMap();
wk.set(name, "JS");
wk.set(person, "nice");
console.log(wk.keys()); // TypeError: wk.keys is not a function
console.log(wk.values()); // TypeError: wk.values is not a function
console.log(wk.entries());  // TypeError: wk.entries is not a function

let map = new Map();
map.set(person, "nice");
console.log(map.keys()); // [Map Iterator] { 'LeBron', { name: 'LeBron', age: 21 } }
console.log(map.values()); // [Map Iterator] { 'JS', 'nice' }
console.log(map.entries()); // [Map Entries] {
                           //  [ 'LeBron', 'JS' ],
                           //  [ { name: 'LeBron', age: 21 }, 'nice' ]
                          //  }
  1. The Map strongly references the key. Even if the key is marked null, the key will not be referenced by GC because the Map’s key/value array is still referenced.
  • WeakMap makes a weak reference to the key, after the key is marked as NULL. Since it is a weak reference, there is no key/value array reference, which does not affect the GC of the key.
  • The following programs require a manual GC startup method:node --expose-gc xxx

Map

function memmorySizeLogger() { global.gc(); const used = process.memoryUsage().heapUsed; console.log((used / 1024 / 1024).toFixed(2) + "M"); } memmorySizeLogger(); // 1.79M let person = {name: "Leon ", age: 21, TMP: new Array(5 * 1024 * 1024),}; memmorySizeLogger(); // 41.96m let map = new map (); memmorySizeLogger(); / / 41.96 M map. The set (person, "nice"); memmorySizeLogger(); // 41.96m person = null; memmorySizeLogger(); // The 41.96M person memory is not reclaimed

If you want GC to work normally in this case, you need to execute map.delete(person) before marking it NULL.

WeakMap

function memmorySizeLogger() { global.gc(); const used = process.memoryUsage().heapUsed; console.log((used / 1024 / 1024).toFixed(2) + "M"); } memmorySizeLogger(); // 1.79M let person = {name: "Leon ", age: 21, TMP: new Array(5 * 1024 * 1024),}; memmorySizeLogger(); // 4.1M let wk = new WeakMap(); memmorySizeLogger(); 41.96 M / / wk. A set (person, "nice"); memmorySizeLogger(); // 41.96m person = null; memmorySizeLogger(); // 1.96M Person memory is being reclaimed

According to WeakMap?

  • In JS, Map API shares two arrays (key and value), and sets the key and value to the end of these two arrays, and generates a reference to the key. When valuing from map, we need to traverse all the keys, and then extract the corresponding index value from the value array through the index.

    • One disadvantage:

      Assignment and search are O(n) complex

    • Disadvantages. 2:

      Using a Map is prone to memory leaks because the array references each key and value all the time, causing normal GC.

  • WeakMap weakly references Key, which does not affect normal GC

    • The key is invalidated after GC
  • If you want to add data to an object without interfering with the garbage collection mechanism, you can use WeakMap

    • If you need to traverse/iterate, you need to use a Map

Application scenarios

Save the DOM node data

let domData = new WeakMap();

let dom = document.getElementById("xxx");

const anyDomData = getDomData(dom);
domData.set(dom, anyDomData);
console.log(domData.get(dom)); 

dom.parentNode.removeChild(dom);
dom = null;

Cache related data

let cache = new WeakMap();

class HandleCache {
  get(key) {
    if (cache.has(key)) {
      return cache.get(key);
    } else {
      return undefined;
    }
  }
  set(key, value) {
    cache.set(key, value)
  }
  delete(key){
    cache.delete(key)
  }
}

Encapsulating private properties

let privateData = new WeakMap(); class Person{ constructor(name, age){ privateData.set(this,{name, age}); } getData(){ return privateData.get(this); }}

WeakSet

Front knowledge
JavaScript GC garbage collection mechanism

What is a WeakSet

Weakset object is a collection of some object values, and each object can only appear once, and it is unique in the Weakset

methods

  • add(value)

Add a new element value to the WeakSet object

  • delete(value)

After deleting the element value in the WeakSet object, the has method returns false.

  • has(value)

Returns a Boolean value indicating whether the given value exists in this WeakSet

What’s the difference between “Set” and “Set”

  1. The value of a Set can be any value, and the value of a WeakSet can only be an object
const name = "LeBron";
const age = 21;
const person = {
  name: "LeBron",
  age: 21,
};

const ws = new WeakSet();
const set = new Set();

set.add(name);
set.add(age);
set.add(person); 

ws.add(person);
ws.add(name); // TypeError: Invalid value used in weak set
ws.add(age);    //  TypeError: Invalid value used in weak set
  1. SET is traversable, and WeakSet is not traversable
  • Set contains an array of values that refer to the original object, so it is traversable
  • Weakset does not store such an array, so it cannot be traversed
const name = "LeBron";
const age = 21;
const person = {
  name: "LeBron",
  age: 21,
};

const ws = new WeakSet();
const set = new Set();

set.add(name);
set.add(age);
set.add(person);
console.log(set.values()); // { 'LeBron', 21, { name: 'LeBron', age: 21 } }

ws.add(person);
ws.add(name); 
ws.add(age);   
console.log(set.values());  // TypeError: ws.values is not a function
  1. SET affects GC, while WeakSET does not
  • The following programs require a manual GC startup method:node --expose-gc xxx

Set contains a values array. After the original value points to null, the values array still has a strong reference to the value, which affects normal GC

function memmorySizeLogger() {
  global.gc();
  const used = process.memoryUsage().heapUsed;
  console.log((used / 1024 / 1024).toFixed(2) + "M");
}

memmorySizeLogger(); // 1.79M

let person = {
  name: "LeBron",
  age: 21,
  tmp: new Array(5 * 1024 * 1024),
};

memmorySizeLogger(); // 41.96M

const set = new Set();
set.add(person);

memmorySizeLogger(); // 41.96M

person = null;

memmorySizeLogger();  // 41.96M

Weakset does not exist such an array, so it does not affect normal GC

function memmorySizeLogger() { global.gc(); const used = process.memoryUsage().heapUsed; console.log((used / 1024 / 1024).toFixed(2) + "M"); } memmorySizeLogger(); // 1.79M let person = {name: "Leon ", age: 21, TMP: new Array(5 * 1024 * 1024),}; memmorySizeLogger(); // 4.1M const WS = new WeakSet(); ws.add(person); memmorySizeLogger(); // 41.96m person = null; memmorySizeLogger(); / / 1.96 M

Application scenarios

Detecting circular references

Recursively calling a function of itself requires a way to deal with a circular data structure by keeping track of which objects have been processed

Function execRecursively(fn, subject, _refs = null){if(! _refs) _refs = new WeakSet(); If (_refs.has(subject)) return; fn(subject); if("object" === typeof subject){ _refs.add(subject); for(let key in subject) execRecursively(fn, subject[key], _refs); } } const foo = { foo: "Foo", bar: { bar: "Bar" } }; foo.bar.baz = foo; // Loop reference! execRecursively(obj => console.log(obj), foo);

Reflect

Reflect, which translates to Reflect, is a new built-in global object that provides methods to intercept JavaScript actions. These methods are the same as those of the Proxy Handler. Reflect is not a function object, but a static utility like function, like Math, so it is not constructable

Reflect the static method

Specific usage reference:
Reflect MDN document

  • Reflect.apply()
  • Reflect.construct()
  • Reflect.defineProperty()
  • Reflect.deleteProperty()
  • Reflect.get()
  • Reflect.getOwnPropertyDescriptor()
  • Reflect.getPrototypeOf()
  • Reflect.has()
  • Reflect.isExtensible()
  • Reflect.ownKeys()
  • Reflect.preventExtensions()
  • Reflect.set()
  • Reflect.setPrototypeOf()

These methods have the same names as the methods of the Proxy Handler, and some of them have the same names as the methods of the Object, although there are some subtle differences

  • What’s the difference?

    • Reflect’s static method returns a Boolean value when it does the corresponding action

      • Returns true on success
      • The operation fails and returns false
    • Converts regular imperative operations to functional operations, and programming adds metaprogramming.

      • Examples are delete, assignment, judgment, and so on
    • Failure of an imperative operation usually returns an error, while Reflect does not, and returns a Boolean value to determine success.
  • Some reflection APIs already exist in built-in objects, and Reflect aggregates them and optimizes them

What is metaprogramming?

  • Metaprogramming is programming a programming language
  • For example, Proxy objects can act as proxies and intercept GET and SET operations
  • And what you get in the program is the value that you programmed.
  • Reflect is a reflection that calls methods on various built-in objects that have been processed

    • So when the methods of each built-in object are changed, the method called by Reflect is also changed
    • It kind of encapsulates a layer

Reflect the advantages of

  1. Optimizing namespaces

You’ll find that JS’s built-in reflection methods are scattered all over the place, and Reflect has put them all together nicely.

  1. Make your code more robust

Operation with Reflect makes it less likely that exceptions will be thrown and threads will block, making the code run more robuly.

  1. Why not just hang on Object?
  • Objects are reflected not only against objects, but also possibly against functions

    • For example apply, calling object.apply (myFunc) is a bit odd
  • Keeping built-in methods in a single object keeps the purity of other objects in your JavaScript code

    • This is better than mounting direct reflection onto a constructor or primitive
    • Better than using global variables directly, so that JS keywords will be more and more.
  • The Denver nuggets in the original
  • Nuggets: Front end LeBron
  • Zhihu: Front-end Leon
  • Continue to share technical blog posts, pay attention to WeChat public number 👉 front-end Leon