Writing is not easy, without the permission of the author forbid to reprint in any form! If you think the article is good, welcome to follow, like and share! Continue to share technical blog posts, follow the wechat public account 👉🏻 front-end LeBron

WeakMap

JavaScript GC garbage collection mechanism

What is WeakMap?

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

methods

  • set(key, value)

Set a set of associated objects in A WeakMap and return a WeakMap object

  • get(key)

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

  • has(key)

Return a Boolean value based on whether there is a key associated object

  • delete(key)

Removes the object associated with key, and returns false after the has(key) method

How is it different from Map?

  1. The Map key or value can be of any type
  • WeakMap keys can only be objects, and value can be any type
const name = "LeBron";
const person = {
  name: "LeBron".age: 21};let wk = new WeakMap(a); 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(a); map.set(name,"JS");
map.set(person, "nice");
console.log(map.get(name)); // JS
console.log(map.get(person)); // nice
Copy the code
  1. A Map’s key/value is traversable because its key/value is stored in an array.
  • WeakMap does not have an array to store key/value, so it cannot be traversed.
const name = "LeBron";
const person = {
  name: "LeBron".age: 21};let wk = new WeakMap(a); 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(a); 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' ]
                          / /}
Copy the code
  1. A Map strongly references a key. Even if the key is marked null, the key is not GC because the Map’s key/value array is still referenced.
  • WeakMap weakly references a 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 program requires 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.79 M

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

let map = new Map(a); memmorySizeLogger();/ / 41.96 M

map.set(person, "nice");
memmorySizeLogger(); / / 41.96 M

person = null;
memmorySizeLogger();  // The memory of 41.96M person was not reclaimed
Copy the code

If you want a normal GC in this case, you need to execute map.delete(person) before marking null.

WeakMap

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

memmorySizeLogger();  / / 1.79 M

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

let wk = new WeakMap(a); memmorySizeLogger();/ / 41.96 M

wk.set(person, "nice");
memmorySizeLogger();  / / 41.96 M

person = null;
memmorySizeLogger();  // 1.96m person memory was reclaimed
Copy the code

According to WeakMap?

  • The Map API in JS shares two arrays (key and value). The set key and value are appended to the end of the two arrays, and the reference to the key is generated. When evaluating from a map, you need to iterate through all the keys and then retrieve the corresponding index value from the value array through the index.
    • One disadvantage:

Assignment and search are O(n) complexity

  • Disadvantages. 2:

Using maps is prone to memory leaks because arrays reference every key and value all the time, making normal GC impossible.

  • WeakMap makes weak reference to key, which does not affect normal GC
    • The key becomes invalid after being GC
  • WeakMap can be used if you want to add data to an object without interfering with garbage collection
    • If you need to iterate/iterate, you need to use Map

Application scenarios

Save DOM node data

let domData = new WeakMap(a);let dom = document.getElementById("xxx");

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

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

Caching related data

let cache = new WeakMap(a);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)
  }
}
Copy the code

Encapsulating private properties

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

WeakSet

JavaScript GC garbage collection mechanism

What is a WeakSet

A WeakSet object is a collection of some object values, and each object can only appear once and is unique in a WeakSet

methods

  • add(value)

Add a new element value to the WeakSet object

  • delete(value)

When the Value element is removed from the WeakSet object, the HAS method returns false.

  • has(value)

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

What’s the difference with Set

  1. The value of a Set can be any value, while 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(a);const set = new Set(a); 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
Copy the code
  1. Set is traversable, while WeakSet is not
  • Set contains an array of values that reference the original object, so it can be traversed
  • 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(a);const set = new Set(a); 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
Copy the code
  1. Set affects GC, while WeakSet does not
  • The following programs require a manual GC startup method:node --expose-gc xxx

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

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

memmorySizeLogger(); / / 1.79 M

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

const set = new Set(a); set.add(person); memmorySizeLogger();/ / 41.96 M

person = null;

memmorySizeLogger();  / / 41.96 M
Copy the code

WeakSet does not have such 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.79 M

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

const ws = new WeakSet(a); ws.add(person); memmorySizeLogger();/ / 41.96 M

person = null;

memmorySizeLogger();  / / 1.96 M
Copy the code

Application scenarios

Detection loop reference

Recursively calling functions on their own requires a way to deal with circular data structures by keeping track of which objects have been processed

// Perform a callback on all contents stored internally in the subject object passed in
function execRecursively(fn, subject, _refs = null){
        if(! _refs) _refs =new WeakSet(a);// Avoid infinite recursion
        if(_refs.has(subject))
                return;

        fn(subject);
        if("object"= = =typeof subject){
                _refs.add(subject);
                for(let key insubject) execRecursively(fn, subject[key], _refs); }}const foo = {
        foo: "Foo".bar: {
                bar: "Bar"}}; foo.bar.baz = foo;// loop reference!
execRecursively(obj= > console.log(obj), foo);
Copy the code

Reflect

Reflect, translated as reflection, is a new built-in global object that provides methods for intercepting JavaScript operations. These methods are the same as those used by Proxy Handlers. Reflect is not a function object, it is a static tool-like function, like Math, so it is not constructible

Reflect’s static method

Usage reference: Reflect MDN documentation

  • 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 are named the same as Proxy Handler methods, and some of them are the same as Object methods, although there are some subtle differences

  • What’s the difference?
    • Reflect’s static method returns a Boolean when it does the corresponding operation
      • Returns true on success
      • Return false on failure
    • The routine imperative operation is transformed into functional operation, and the programming method increases the metaprogramming.
      • Examples include delete, assignment, judgment, and so on
    • Reflect does not return a Boolean to determine whether an imperative operation has succeeded.
  • 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, the Proxy object can be used to 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 each built-in object after it has been processed
    • So when the methods of each built-in object change, Reflect calls the same method
    • It’s kind of encapsulated

Reflect the advantages of

  1. Optimizing the namespace

You’ll find JS’s built-in reflection methods scattered all over the place, and Reflect organizes them nicely.

  1. Enhance code robustness

Working with Reflect makes it less likely that exceptions will be thrown, threads will block, and code will run more robustingly.

  1. Why not just hang it on Object?
  • Reflected objects are not only for objects, but also for functions
    • For example apply, calling Object.apply(myFunc) is strange
  • Storing built-in methods in a single object keeps the rest of your JavaScript code pure
    • This is preferable to direct reflection mounted on constructors or primitives

    • It is better to use global variables directly, so JS keywords will be more and more.


  • Nuggets: Front-end LeBron

  • Zhihu: Front-end LeBron

  • Continue to share technical blog posts, follow the wechat public account 👉🏻 front-end LeBron