The original intention of this paper is the precipitation and summary of the two data types, Map and Set, and their related types. Hopefully, it will also be an introduction to others.
The index
- Map
- WeakMap
- Set
- WeakSet
- reference
Map
define
- The new collection type truly implements the storage mechanism in the form of key-value pairs.
Basic API
Initialize the
-
Unlike objects, which can be created using Object literals, maps are created using constructors.
-
The arguments passed must be iterable objects.
// No parameter const m1 = new Map(a);// Iterable parameters const m2 = new Map([['key1'.'value1'], ['key2'.'value2']]);// Customize iterable parameters const m3 = new Map({[Symbol.iterator]: function* (){ yield ['key1'.'value1']; yield ['key2'.'value2']; yield ['key3'.'value3']; }});Copy the code
-
The key pairs of M2 and m3 are equal, but they themselves are not equal.
-
Use the size attribute to see the length of the key-value pair.
m1.size; / / 0 m2.size; / / 2 m3.size; / / 3 Copy the code
set
-
If you want to add values after the Map is initialized, use the set method. It takes keys and values as arguments in order.
-
Unlike Object, which can be set to any data type, neither the key nor the value is of any data type.
-
And after the call, the current object is returned, that is, it supports chained calls.
m1.set('myKey1'.'myValue2'); const symbolKey = Symbol(a);const functionKey = function(){}; const booleanKey = false; const objectKey = {}; m1.set(symbolKey, 'symbol'); m1.set(functionKey, 'function'); // String calls m1.set(booleanKey, true).set(objectKey, "{}"); m1.size; / / 5 Copy the code
get
-
Map values also have specific methods, and cannot be obtained directly in the form of access like objects.
m1.get(symbolKey); m1.get(functionKey); // Change the attribute of a key or value without affecting the key value objectKey.key = 'key'; m1.get(objectKey); / / "{}" Copy the code
has
-
The Map also have their own way to judge whether there is a key, similar to the Object. The prototype. HasOwnProperty.
// Obviously not m1.has(123); // false m1.has(symbolKey); // true m1.has(objectKey); // true // Does not refer to the same reference m1.has(Symbol()); // false m1.has({}); // false Copy the code
delete
-
Map deletes the property using the delete method and returns a Boolean value indicating whether the key exists in the current key-value pair. In other words, it can also be used to detect whether the key exists, similar to has.
m1.delete(functionKey); // true m1.delete(456); // false m1.size; / / 4 Copy the code
clear
-
Map clears all key-value pairs using the clear method.
m1.clear(); m1.size; / / 0 Copy the code
Sequence and iteration
- Maps are iterable and remember the order in which key-value pairs were inserted.
entries
-
Get the key-value pairs in sequence.
for (let pair of m2.entries()) { console.info(pair); // Output ['key1', 'value1'], etc } for (let pair of m2[Symbol.iterator]) { console.info(pair); // Output ['key1', 'value1'], etc } Copy the code
keys
-
Get the keys in order.
-
The current key can be modified during the traversal.
- The original string cannot be modified.
- The new key does not affect the original key value.
for (let key of m2.keys()) { console.info(key); // key1 key2 key = 'kkk'; console.info(m2.get(key), m2.get('key1')); // undefined 'value1' } Copy the code
values
-
Get the values sequentially.
-
The current value can be modified during the iterate. It’s similar to a bond.
- The original string cannot be modified.
- The modified value can be retrieved using the original key.
for (let value of m2.values()) { console.info(value); // value1 value2 value = 'val'; console.info(m2.get('key1'), value); // 'value1' 'val' } Copy the code
forEach
-
It is worth noting that the first argument of the traversal is the value and the second argument is the key.
m2.forEach((value, key) = > { console.info(value, key); // Output ['value1', 'key1'], etc }) Copy the code
Map and Object selection
- For most developers, it’s more of a personal preference. The gap between the two is not as great as one might think.
Comparison of advantages and disadvantages
- Memory footprint: Given a fixed size of memory, a Map can store 50% more memory
- Insert performance: Maps perform slightly better when a large number of inserts are involved.
- Search speed: Object is better when a large number of searches are involved. (Perhaps because Map maintains order)
- Delete performance: Object delete has been criticized for its performance in different browsers, so Map is better.
Weak WeakMap mapping
define
- Only objects can be used as key mappings, and when the key object loses reference, it will be garbage collected.
Basic API
Initialize the
-
The initialization method is similar to that of Map.
const wm1 = new WeakMap(a);const wm2 = new WeakMap([[{},'{}'], [objectKey, {}] ]); // The following declaration will report an error because only objects can be used as keys const wm3 = new WeakMap([[12.34], ['str'.'string']]);// Thrown exception: Uncaught TypeError: Invalid value used as weak map key Copy the code
set/get/has/delete
-
WeakMap can also use these methods, but it does not support clear, nor does it have an iterative method, nor does it have a size attribute.
-
However, you can use the way to redefine a WeakMap to simulate the clear method.
class ClearableWeakMap { constructor(init) { this._wm = new WeakMap(init); } clear() { return new WeakMap(a); }delete(key) { return this._wm.delete(key); } set(key, val) { this._wm.set(key, val); return this; } has(key) { return this._wm.has(key); }}Copy the code
Key recovery
-
Let’s look at a specific case where the key is recycled.
-
It is important to note that because garbage collection timing is difficult to verify, the results may be inaccurate when performed in a regular JS environment. You can try it manually by clicking Collect Garbage in Chrome’s Devtools.
-
Or if the following case is executed in the Node environment and the in-memory data is directly available, you can see the exact effect.
// If the following weak mapping is defined, the value will never actually be obtained const wm3 = new WeakMap([[{}, {key: 'val '}]]);// There is actually no corresponding reference, it is empty wm3.get({}); // undefined // The reference is cleared const container = { key: {}}function removeReference() { container.key = null; } const m4 = new Map([ [container.key, 'val']]);const wm4 = new WeakMap(a); wm4.set(container.key,'val'); console.info(wm4.get(container.key), m4.get(container.key)); // There are key-value pairs removeReference(); console.info(wm4.get(container.key), m4.get(container.key)); // wm has no key-value pairs Copy the code
This section describes the usage scenario of WeakMap
Deploying private variables
-
Use closures to prevent WM from being accessed externally.
const User = (() = > { const wm = new WeakMap(a);class User { constructor(id) { this.idProperty = Symbol('id'); this.setId(id); } setPrivate(property, value) { const privateMembers = wm.get(this) | | {}; privateMembers[property] = value; wm.set(this, privateMembers); } getPrivate(property) { const privateMembers = wm.get(this) | | {};return privateMembers[property]; } setId(id) { return this.setPrivate(this.idProperty, id); } getId(){ return this.getPrivate(this.idProperty); }}returnUser; }) ();const userA = new User('biaomianshiluanma'); Copy the code
Stores DOM metadata
-
If the button is removed from the page, the reference is still there, taking up unnecessary memory, so it is better to use a weak mapping.
const btnEle = document.querySelector('#btn'); const domWm = new WeakMap(a);const domM = new Map(a); domwWm.set(btnEle, {disable: true }); domM.set(btnEle, { disable: true }); // When the DOM node is deleted from the page, the data stored in domWm is automatically reclaimed Copy the code
Set
define
- Collection type: A collection of arbitrary values.
- Values can be of any type, but they are always unique.
Basic API
- Note that there is no way to value a Set.
Initialize the
-
Initialize a collection using an array.
-
It can also be initialized using an iterator.
const s1 = new Set(['val1'.'val2'.'val3']); const s2 = new Set({[Symbol.iterator]: function* () { yield "val1"; yield "val2"; yield "val3"; }}); s1.size;/ / 3 s2.size; / / 3 Copy the code
add
-
After the Set is initialized, you want to continue adding values. You can use the add method, and the arguments can be of any type.
-
At the same time, it returns the currently invoked object, which means it also supports chained calls.
const arr = [1.2.3.4]; s1.add(4); s1.add({}); s1.add(arr); s1.add(true); s2.add(4).add('5'); s1.size; / / 7 s2.size; / / 5 Copy the code
has
-
Set also has its own method to determine if a key is present, similar to array.prototype.includes.
s1.has(4); // true s1.has('4'); // false s1.has(arr); // true s1.has({}); // false does not refer to the same memory address s2.has('5'); // true Copy the code
-
Similar to Map, if you modify the properties of an object in a Set, the has method does not affect the judgment.
const s3 = new Set(a);let obj = { name: 'kk' }; s3.add(obj); obj.age = 29; // Only properties are updated s3.has(obj); // true obj = { alias: 'hh' }; // Updated the reference s3.has(obj); // false Copy the code
delete
-
The Set is also deleted by delete, which also returns a Boolean value indicating whether the key exists.
s2.delete(123123); // false s2.delete(2); // true Copy the code
clear
-
Set also uses clear to clear all values.
s3.clear(); s3.size; / / 0 Copy the code
Sequence and iteration
- The Set maintains the order in which values are stored.
keys/values
-
Since there is no concept of key-value pairs in a Set, the two return the same on a Set, returning a new iterator object.
// Iteration results are the same. Iteration order is the order of insertion for (let key of s1.keys()) { console.info(key, 'key'); } for (let val of s1.values()) { console.info(val, 'val'); } // val1 // val2 // val3 Copy the code
entries
-
Return as a key-value pair, that is, return two identical values.
for (let o of s1.entries()) { console.info(o, 'entries'); } // Get the following result // ['val1', 'val1'] // ['val2', 'val2'] // ['val3', 'val3'] / / (4, 4) / / / '5', '5' // The following results are similar for (let [k, v] of s1.entries()) { console.info(k, v, 'entries'); } Copy the code
forEach
-
Just like iterating through a Set, you can use forEach. To get results similar to the keys/values method.
s1.forEach((value) = > console.info(value)); // val1 // val2 // val3 / / 4 / / '5' Copy the code
Application scenarios of Set
-
The uniqueness of values makes sets a good place for reprocessing, as well as for getting intersections, unions, etc.
-
Here is the implementation:
class XSet extends Set { isSuperXSet(set) { return XSet.isSuperXSet(this, set); } union(. sets) { return XSet.union(this. sets); }intersection(. sets) { return XSet.intersection(this. sets); }difference(sets) { return XSet.difference(this, sets); } symmtricDifference(sets) { return XSet.symmtricDifference(this, sets); } powerSet() { return XSet.powerSet(this); } /** is a subset * of the other@param setA * @param setB * @returns * / static isSuperXSet(setA, setB) { for (const val of setA) { if(! setB.has(val)) {return false; }}return true; } /** * return union *@param setA * @param setsB * @returns * / static union(setA, ... setsB) { const unionSet = new XSet(setA); for (const valA of setA.values()) { for (const valB of setsB) { if (valB.has(valA)) { continue; } unionSet.add(valA); }}return unionSet; } /** * return intersection *@param setA * @param setsB * @returns * / static intersection(setA, ... setsB) { const intersectionSet = new XSet(setA); for (const valA of setA) { for (const bSet of setsB) { if (bSet.has(valA)) { continue; } intersectionSet.delete(valA); }}return intersectionSet; } /** * returns the difference set@param setA * @param setB * @returns * / static difference(setA, setB) { const differenceSet = new XSet(setA); for (const b of setB) { if(differenceSet.has(b)) { differenceSet.delete(b); }}return differenceSet; } /** * Symmetric difference set *@param setA * @param setB * @returns * / static symmtricDifference(setA, setB) { returnsetA.union(setB).difference(setA.intersection(setB)); }}// After instantiating, it is ready to use const xSet = new XSet([1.2.3.4]); const xSetChild = new XSet([1.2.3]); xSetChild.isSuperXSet(xSet); // true Copy the code
WeakSet weak collections
define
- Weak collections. The value can only be
Object
Type or inherited fromObject
Type. - An attempt to set a value that is not an object throws an exception
TypeError
.
Basic API
- With the Set type, it also has these apis, but less
clear
Methods. You can simulate it by redefining a WeakSetclear
.
add
has
delete
Weak value
-
The particularity of weak collections makes them convenient for garbage collection.
// Declare an empty object with no other reference to the object const wSet = new WeakSet([{}]); // After the declaration, the empty object is garbage collected, making the contents of the wSet empty console.info(wSet); // Sometimes there may be content printed out, it may be that the garbage collection of the browser has not been triggered, see the WeakMap introduced above Copy the code
-
Here’s an example that might be more intuitive.
const container = { val: {}};const wSet1 = new WeakSet([container.val]); /** * Remove references */ function removeReferrence() { container.val = null; } console.info(wSet1, wSet1.has(container.val)); // true removeReferrence(); console.info(wSet1, wSet1.has(container.val)); // false Copy the code
noniterability
- Because the values in a weak set are weak and can be destroyed at any time, there is no need to provide the ability to iterate.
- The reason why WeakSet restricts only objects as values is to ensure that the value can be obtained only by reference to the value object. If raw values are allowed, there is no way to distinguish between a string literal used during initialization and an equivalent string used after initialization.
Application scenarios of WeakSet
-
WeakSet can also be used to store DOM metadata.
const disabledElements = new WeakSet(a);const loginBtn = document.querySelector('#login'); DisabledElements can free the corresponding memory as long as any button is removed from the page disabledElements.add(loginBtn); Copy the code
reference
Advanced Programming in JavaScript (version 4)
Some references