WeakMap, WeakMap, Set, WeakSet

Relevant videos have been made in this article. Click to jump to station B: Deep and simple Map, WeakMap, Set, WeakSet

Map

Prior to ES6, key-value pair storage was implemented with an Object Object:

var obj = {
    key: "val"
}
Copy the code

ES6 implements a true key-value pair store: Map

Basic API

  1. Instantiation:new Map()

We can instantiate a Map object with new:

const m = new Map(a)Copy the code

Of course, if we want to add data at instantiation time, we can add an iterable object, such as an array object:

const m = new Map([['k1'.'v1'],
    ['k2'.'v2'],
    ['k3'.'v3']])console.log(m.size) / / > 3
Copy the code
const m = new Map({[Symbol.iterator]: function* () {
        yield ['k1'.'v1']
        yield ['k2'.'v2']
        yield ['k3'.'v3']}})console.log(m.size) / / > 3
Copy the code
  1. set(k, v), adds a new key-value pair, and if the key already exists, overwrites its old value with the new value
const m = new Map()
m.set('k1'.'v1')

The set() method returns the current Map object, so it can be called chained
m.set('k2'.'v2')
    .set('k3'.'v3')
Copy the code
  1. get(k)And get its corresponding value based on the key
  2. has(k)To check whether the key exists
const m = new Map()
m.set('k1'.'v1')
    .set('k2'.'v2')
    .set('k3'.'v3')

console.log(m.has('k1')) //> true
console.log(m.get('k1')) //> v1
console.log(m.get('k4')) //> undefined
Copy the code
  1. size, get the currentMapThe number of key-value pairs of an object
  2. delete(k): Deletes the specified key-value pair
  3. clear()Empty:mapObject all key-value pairs

traverse

Map maintains the insertion order and traverses according to that order

const m = new Map()
m.set('k1'.'v1')
    .set('k2'.'v2')
    .set('k3'.'v3')
// Method 1:
for (const e of m) {
    console.log(e)
}
//> [ 'k1', 'v1' ]
//> [ 'k2', 'v2' ]
//> [ 'k3', 'v3' ]

// Method 2:
for (const e of m.entries()) {
    console.log(e)
}
//> [ 'k1', 'v1' ]
//> [ 'k2', 'v2' ]
//> [ 'k3', 'v3' ]

// Method 3:
for (const e of m[Symbol.iterator]()) {
    console.log(e)
}
//> [ 'k1', 'v1' ]
//> [ 'k2', 'v2' ]
//> [ 'k3', 'v3' ]

// In fact, mode 2 and mode 3 call the same method
console.log(m.entries === m[Symbol.iterator])
//> true

// Method 4:
m.forEach((v, k) = > {
    console.log([k, v])
})
//> [ 'k1', 'v1' ]
//> [ 'k2', 'v2' ]
//> [ 'k3', 'v3' ]

// Method 5: only iterate over the key
for (const k of m.keys()) {
    console.log(k)
}
//> k1
//> k2
//> k3


// Method 6: Only iterate over values
for (const v of m.values()) {
    console.log(v)
}
//> v1
//> v2
//> v3
Copy the code

Select Map or Object

Map and Object are both used for key-value storage. We need to understand the differences between them so that we can choose between them during development

  1. Memory overhead: At the engine level,MapandObjectIn general, the memory overhead for key-value pair storage increases linearly with the number of key-value pairs. But for most browsers,MapIt’s still a little bit more expensive in terms of memory, so roughly,MapthanObject50% less memory overhead, which means the same amount of memory,MapMore key-value pairs can be stored.
  2. Insert performance: ForMapandObject, an insert operation is not affected by the number of key-value pairs, butMapWill be slightly faster thanObject, if your code has a lot of insert operations, recommend this optionMap.
  3. Query performance: In some cases, the browser is optimizedObjectThis is not possible in a Map, so it is recommended if you have a large number of queriesObject.
  4. Delete performance: Deleting is a horrible thing, so if you need to delete it, use pseudo-deleting — assign this property toundefinedornull. If there is a need to delete,MapThe delete performance is faster.

WeakMap

Basic API

  1. instantiation

The key of WeakMap Object can only be the Object instantiated by **Object or the Object derived from **, if not, an error will be reported; The value of a WeakMap object can be of any type:

// 1. Object Instantiated Object
const k1 = new Object(a)// 2. Use literal objects, which are actually instantiated by Object
const k2 = {}

// 3. Object An Object of a derived class
class Other extends Object{}
const k3 = new Other()

// WeakMap value can be any base type or reference object
const wm = new WeakMap([
    [k1, 10086],
    [k2, "I am China Moblie"],
    [k3, new Array("Interesting")]])// If the key does not conform to the specification, an error is reported
const badWM = new WeakMap([[1.2],
    ["Yoo"."Ugh"]])//> TypeError: Invalid value used as weak map key
Copy the code

WeakMap’s other APIS are basically the same as Map objects, but note that there is no size attribute or clear() method

  1. set(k, v), adds a new key-value pair, and if the key already exists, overwrites its old value with the new value
  2. get(k)And get its corresponding value based on the key
  3. has(k)To check whether the key exists
  4. delete(k): Deletes the specified key-value pair

Weak bond

A WeakMap key can only be an Object instantiated Object or a derived Object. The purpose of WeakMap is to make the key weakly held.

Suppose we have a scenario where we need to store the attributes of a DOM node and its values. We might use either Object or Map. Suppose we use Map:

const m = new Map(a)// Suppose we need to save a login button property value
const loginButton = document.querySelector("#login")
m.set(loginButton, {disabled: true})
Copy the code

This creates a problem: When the user logs in and goes to another page, the loginButton is removed. Normally, the login DOM node should and should be cleared by the garbage collector, but it’s referenced by the loginButton variable, and the loginButton is referenced by the map as a key, so the login DOM node stays in memory. Take up space for nothing.

The solution is to manually dereference the pair, either by using the DELETE method or by waiting for the Map object to be destroyed.

If we use a WeakMap object for the same storage:

const wm = new WeakMap(a)const loginButton = document.querySelector("#login")
wm.set(loginButton, {disabled: true})
Copy the code

As the key of WeakMap object, loginButton cannot be counted as formal reference, that is to say, the loginButton variable cannot be referenced by WM. The garbage collector can then kill both the loginButton variable and the login DOM node, freeing up memory.

This has the effect of automatic cleaning, which is also the purpose of WeakMap weak holding.

Non-iterable key

WeakMap key is not a formal reference, which may be recovered and cleared at any time. Therefore, WeakMap does not provide iterative function.

The size attribute and the clear() method are also unavailable because they need to iterate over all the keys first.

Set

ECAMScript6 introduces the Set type, which is consistent with the concepts of sets we learned in high school — deterministic, heterogeneous, and unordered.

Maybe Set is a little confused with the Set () method of Map. Set means Set, noun; Map.set() is a set. It’s a verb.

Basic API

  1. Instantiation. withMapAdd an iterable to the constructor as initialization data.
const set1 = new Set(a)const set2 = new Set(['v1'.'v2'.'v3'])

console.log(set2.size)
/ / > 3

const set3 = new Set({[Symbol.iterator]: function* () {
            yield 'v1'
            yield 'v2'
            yield 'v3'}})console.log(set3.size)
/ / > 3
Copy the code
  1. Add () to add data

    const s = new Set(a)// add() returns the current Set object, so it can be called chained
    s.add('h').add('e').add('l').add('l').add('o')
    Copy the code
  2. Has (v) determines whether the value exists

  3. Delete (v) : deletes the value

  4. Clear (), delete all values in the Set object

  5. Size, returns the number of values in the current Set

traverse

Like a Map, a Set maintains an insertion order and is traversed in that order.

const s = new Set()
s.add('h').add('e').add('l').add('l').add('o')

// Method 1:
for (const v of s) {
    console.log(v)
}
//> h
//> e
//> l //! Notice there's only one L here, and that's because of the heterogeneity of the set, every element is different
//> o

// Method 2:
for (const v of s.values()) {
    console.log(v)
}
/ / or
for (const v of s.keys()) {
    console.log(v)
}
//> h
//> e
//> l 
//> o

// Method 3:
for (const v of s[Symbol.iterator]()) {
    console.log(v)
}
//> h
//> e
//> l 
//> o

/ /! All three methods call the same method
console.log(s.values === s[Symbol.iterator])
console.log(s.keys === s[Symbol.iterator])

// Method 4:
for (const pair of s.entries()) {
    console.log(pair)
}
//> [ 'h', 'h' ] //! The key and the value are equal
//> [ 'e', 'e' ]
//> [ 'l', 'l' ]
//> [ 'o', 'o' ]

// Method 5:
s.forEach((val, sameVal) = > {
    console.log(`[ '${val}', '${sameVal}' ]`)})//> [ 'h', 'h' ]
//> [ 'e', 'e' ]
//> [ 'l', 'l' ]
//> [ 'o', 'o' ]
Copy the code

WeakSet

WeakSet and WeakMap are basically the same, the value stored can only be the Object instantiated by **Object or the Object of derived class **, and cannot be iterated, and there is no clear() method and size attribute.