conclusion

  • 1.MapandSetObject references are strongly typed, andGarbage collection will not be allowed.
  • 2,MapandSetThe traversal order of is the insertion order
  • 3,The key WeakMapandWeakSetBoth are weak references, i.eThe garbage collection mechanism does not consider WeakSet's reference to this object
  • 4,WeakMapandWeakSetAre allNot traverseThe,There is no sizeAttributes, noneclearmethods
  • 5,WeakMapThe key of must be an object (except null);WeakSetThe store must be an object

Set: Similar to an array, but with unique values and no duplicate values

Similar to an array, but with unique values and no duplicate values.

The Set itself is a constructor used to generate the Set data structure.

const s = new Set(a); [2.3.5.4.5.2.2].forEach(x= > s.add(x));

for (let i of s) {
  console.log(i);
}
// 2 3 5 4
Copy the code

The Set function can take an array

You can take an array (or some other data structure with an Iterable interface) as an argument for initialization.

/ / a
const set = new Set([1.2.3.4.4]);
[...set]
// [1, 2, 3, 4]
console.log(set);//Set { 1, 2, 3, 4 }
Copy the code

/ / two cases
const items = new Set([1.2.3.4.5.5.5.5]);
items.size / / 5


/ / 3
const set = new Set(document.querySelectorAll('div'));
set.size / / 56

/ / similar to
const set = new Set(a);document
 .querySelectorAll('div')
 .forEach(div= > set.add(div));
set.size / / 56
Copy the code

Set to array, string deduplication

Array to heavy

/ / 1
let array=[1.2.3.4.5.5.3.2.1]
array=[...new Set(array)]
console.log(array);//[1, 2, 3, 4, 5]

/ / 2
function dedupe(array) {
  return Array.from(new Set(array));
}
dedupe([1.1.2.3]) / / [1, 2, 3]
Copy the code

String deduplication

var str=[...new Set('ababbc')].join(' ')
console.log(str);// "abc"
Copy the code

Inside a Set, two Nans are equal

When you add a value to a Set, no type conversion occurs, so 5 and “5” are two different values.

The algorithm used to determine whether two values are different inside a Set is called “same-value-zero equality.” It is similar to the exact equality operator (===), with the main difference that NaN is equal to itself, whereas the exact equality operator assumes NaN is not equal to itself.

let set = new Set(a);let a = NaN;
let b = NaN;
set.add(a);
set.add(b);
set // Set {NaN}
Copy the code

Inside a Set, two objects are always unequal

let set = new Set(a); set.add({}); set.size/ / 1

set.add({});
set.size / / 2
Copy the code

Two objects are equal only if they have the same address

let a={};
let b=a;
let set=new Set(a); set.add(a); set.add(b);console.log(set);  //Set { {} }
Copy the code

Properties and methods of a Set instance

Instances of the Set structure have the following properties

Set.prototype.constructor

Constructor, which is Set by default.

Set.prototype.size

Returns the total number of members of a Set instance.

Four operation methods

add(value)

Add a value and return the Set structure itself.

delete(value)

When a value is deleted, a Boolean value is returned indicating whether the deletion was successful.

has(value)

Returns a Boolean value indicating whether the value is a member of Set.

clear()

Clears all members, no return value.

var s=new Set(a); s.add(1).add(2).add(2);
// Notice that 2 is added twice

s.size / / 2

s.has(1) // true
s.has(2) // true
s.has(3) // false

s.delete(2);
s.has(2) // false
s.clear();
console.log(s);  //Set {} size is 0
Copy the code
The Object and Set structures determine whether a key is included
// How to write objects
const properties = {
  'width': 1.'height': 1
};

if (properties[someName]) {
  // do something
}

// Set
const properties = new Set(a); properties.add('width');
properties.add('height');

if (properties.has(someName)) {
  // do something
}

Copy the code
The array. from method converts a Set structure to an Array
const items = new Set([1.2.3.4.5]);
const array = Array.from(items);
console.log(array);//[1, 2, 3, 4, 5]
Copy the code

Four traversal methods

keys()

Returns a traverser for the key name

values()

Returns a traverser for key values

entries()

Returns a traverser of key-value pairs, including both the key name and the key value, printing an array at a time that contains two identical members

forEach()

Use the callback function to iterate over each member

The traversal order of a Set is the insertion order

This feature can be useful, such as using Set to hold a list of callback functions that are guaranteed to be called in the order they were added

Keys (), values(), entries()

Because the Set structure has no key name, only the key value (or the key name and the key value are the same value), the keys and values methods behave exactly the same

let set = new Set(['red'.'green'.'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
Copy the code
forEach()

Like arrays, there are forEach methods that perform some kind of operation on each member and return no value.

The key name of the Set structure is the key value (both are the same value), so the value of the first argument is always the same as the value of the second argument.

let set = new Set([1.4.9]);
set.forEach((value, key) = > console.log(key + ':' + value))
/ / 1:1
/ / 4:4
/ / 9:9
Copy the code

Extended operators (…) Internally use for… Of circulation

So it can also be used with sets.

let set = new Set(['red'.'green'.'blue']);
let arr = [...set];
// ['red', 'green', 'blue']
Copy the code
The map and filter methods of arrays can also be used indirectly for sets

First convert to array -> use Map and filter -> finally convert to Set

let set = new Set([1.2.3]);
set = new Set([...set].map(x= > x * 2));
{2, 4, 6}

let set = new Set([1.2.3.4.5]);
set = new Set([...set].filter(x= > (x % 2) = =0));
Set {2, 4}
Copy the code
Union, Intersect, and Difference
let a = new Set([1.2.3]);
let b = new Set([4.3.2]);

/ / and set
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

/ / intersection
let intersect = new Set([...a].filter(x= > b.has(x)));
// set {2, 3}

/ / difference set
let difference = new Set([...a].filter(x= >! b.has(x)));// Set {1}
Copy the code

WeakSet, similar in structure to Set, is also a collection of non-repeating values

WeakSet structure is similar to Set, which is also a collection of non-repeating values.

WeakSet has two differences from Set

First, WeakSet members can only be objects, not other types of values

Secondly, all objects in WeakSet are weak references, that is, garbage collection mechanism does not consider WeakSet’s reference to the object

In other words, if the object is no longer referenced by other objects, the garbage collection mechanism will automatically reclaim the memory occupied by the object, regardless of the object still exists in the WeakSet.

const ws = new WeakSet(a); ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
Copy the code

WeakSet is suitable for temporarily storing a group of objects and information bound to objects. As soon as the object disappears externally, its reference in WeakSet disappears automatically.

WeakSet and WeakMap cannot be traversed

A WeakSet member is not suitable for reference because it will disappear at any time. In addition, because the number of internal WeakSet members depends on whether garbage collection mechanism is running, the number of members may be different before and after operation, and when garbage collection mechanism is running is unpredictable, so ES6 stipulates that WeakSet cannot be traversed.

These characteristics also apply to the WeakMap structure described later in this chapter.

WeakSet has no size property, so there is no way to traverse its members

Syntax,const ws = new WeakSet();

WeakSet is a constructor that uses the new command to create a WeakSet data structure

const ws = new WeakSet(a);Copy the code

Parameter, which can take an array or an array-like object as a parameter

WeakSet can accept an array or array-like object as a parameter. (In fact, any object with an Iterable interface can be used as a WeakSet argument.) All members of the array automatically become members of a WeakSet instance object.

const a = [[1.2], [3.4]].const ws = new WeakSet(a);
// WeakSet {[1, 2], [3, 4]}
Copy the code

It is the members of the A array that become WeakSet members, not the A array itself

const b = [3.4];
const ws = new WeakSet(b);
// Uncaught TypeError: Invalid value used in weak set(...)
Copy the code

Array B members are not objects, adding WeaKSet will report an error.

Three methods

WeakSet.prototype.add(value)

Add a new member to the WeakSet instance.

WeakSet.prototype.delete(value)

Clears the specified member of a WeakSet instance.

WeakSet.prototype.has(value)

Returns a Boolean value indicating whether a value exists in a WeakSet instance.

const ws = new WeakSet(a);const obj = {};
const foo = {};

ws.add(window);
ws.add(obj);

ws.has(window); // true
ws.has(foo);    // false

ws.delete(window);
ws.has(window);    // false
Copy the code

use

WeakSet is useful for storing DOM nodes without worrying about memory leaks when they are removed from documents.

const foos = new WeakSet(a)class Foo {
  constructor() {
    foos.add(this)
  }
  method () {
    if(! foos.has(this)) {
      throw new TypeError('foo.prototype. method can only be called on instances of Foo! '); }}}Copy the code

The above code ensures that instance methods of Foo can only be called on instances of Foo. The advantage of using WeakSet here is that the reference to the instance by foos is not counted in the memory reclamation mechanism, so when deleting the instance, you do not need to consider foos and there is no memory leak.

A Map is similar to an object, but the “key” is not limited to strings. All types of values (including objects) can be used as keys

Map Data structure. It is a collection of key-value pairs similar to objects, but the range of “keys” is not limited to strings. Values of all types (including objects) can be used as keys.

JavaScript objects are essentially collections of key-value pairs (Hash structures), but traditionally strings can only be used as keys.

The Object structure provides string-value mappings, and the Map structure provides value-value mappings

// Traditional objects
const data = {};
const element = document.getElementById('myDiv');

data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"
// Element is automatically converted to string '[object HTMLDivElement]'
Copy the code
/ / Map object
const m = new Map(a);const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false
Copy the code

parameter

Map takes an array of key-value pairs :[[‘name’, ‘zhang SAN ‘]]

const map = new Map([['name'.'Joe'],
  ['title'.'Author']]); map.size/ / 2
map.has('name') // true
map.get('name') // "/"
map.has('title') // true
map.get('title') // "Author"
Copy the code
Principle:
const items = [
  ['name'.'Joe'],
  ['title'.'Author']].const map = new Map(a); items.forEach(([key, value]) = > map.set(key, value)
);
Copy the code

Both sets and maps can be used to generate new maps

Any data structure that has an Iterator interface and each member is a two-element array can be taken as an argument to the Map constructor, not just an array

const set = new Set([['foo'.1],
  ['bar'.2]]);const m1 = new Map(set);
m1.get('foo') / / 1

const m2 = new Map([['baz'.3]]);
const m3 = new Map(m2);
m3.get('baz') / / 3
Copy the code

Key about Map

1. Assign multiple values to the same key, and subsequent values overwrite previous values (same as the object)

const map = new Map(a); map .set(1.'aaa')
.set(1.'bbb');

map.get(1) // "bbb"
Copy the code

2, read an unknown key, default undefined(same as the object)

new Map().get('asfddfsasadf')
// undefined
Copy the code

3. Only references to the same object are treated as references to the same key –Focus on

That is, only objects that reference the same address (memory address) are the same key

When the Map key is an object, the Map key is actually bound to the memory address. As long as the memory address is different, the Map is treated as two keys.

// The reference address (memory address) is different
const map = new Map(a); map.set(['a'].555);
map.get(['a']) // undefined


// Reference address (memory address) same
var a=['a'];
var b=a;
const map = new Map(a); map.set(a,555);
console.log(map.get(b)); / / 555
Copy the code

4. When a Map’s key is a value of a simple type (number, string, Boolean), as long as the two values are exactly equal (===), the Map treats them as one key

So 0 and minus 0 are a bond
The Boolean true and the string true are two different keys
Undefined and NULL are also two different keys
Although NaN is not strictly equal to itself, Map treats it as the same key
let map = new Map(a); map.set(-0.123);
map.get(+0) / / 123

map.set(true.1);
map.set('true'.2);
map.get(true) / / 1

map.set(undefined.3);
map.set(null.4);
map.get(undefined) / / 3

map.set(NaN.123);
map.get(NaN) / / 123
Copy the code

Properties and operation methods

The size attribute

Total number of Map structure members

const map = new Map(a); map.set('foo'.true);
map.set('bar'.false);
map.size / / 2
Copy the code

set(key, value)

Set the key corresponding to key to value

Returns the entire Map structure

If the key already has a value, the key value is updated, otherwise the key is generated.

const m = new Map(a); m.set('edition'.6)        // keys are strings
m.set(262.'standard')     // Keys are numeric values
m.set(undefined.'nah')    / / key is undefined
Copy the code

The set method returns the current Map object, so it can be chained.

let map = new Map()
  .set(1.'a')
  .set(2.'b')
  .set(3.'c');
console.log(map);  //Map { 1 => 'a', 2 => 'b', 3 => 'c' }
Copy the code

get(key)

Read the value of key. If key is not found, return undefined.

const m = new Map(a);const hello = function() {console.log('hello'); }; m.set(hello,'Hello ES6! ') // Keys are functions

m.get(hello)  // Hello ES6!
Copy the code

has(key)

Returns a Boolean value indicating whether a key is in the current Map object.

const m = new Map(a); m.set('edition'.6);
m.set(262.'standard');
m.set(undefined.'nah');

m.has('edition')     // true
m.has('years')       // false
m.has(262)           // true
m.has(undefined)     // true
Copy the code

delete(key)

The delete method deletes a key,

Returns true. If deletion fails, return false.

const m = new Map(a); m.set(undefined.'nah');
m.has(undefined)     // true

m.delete(undefined)
m.has(undefined)       // false
Copy the code

clear()

The clear method clears all members

No return value.

let map = new Map(a); map.set('foo'.true);
map.set('bar'.false);

map.size / / 2
map.clear()
map.size / / 0
Copy the code

The Map of traverse

The traversal order of a Map is the insertion order

Keys () :

Returns a traverser for the key name.

Values () :

Returns a traverser for key values.

Entries () :

Returns a traverser for all members (key-value pairs).

The forEach () :

Use the callback function to traverse all members of the Map.

const map = new Map([['F'.'no'],
  ['T'.'yes']]);for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

/ / or
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

// Equivalent to using map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"
Copy the code

Map forEach method

The array forEach method is similar in that it can be traversed

map.forEach(function(value, key, map) {
  console.log("Key: %s, Value: %s", key, value);
});
Copy the code
The forEach method can also take a second argument that binds this
const reporter = {
  report: function(key, value) {
    console.log("Key: %s, Value: %s", key, value); }}; map.forEach(function(value, key, map) {
  this.report(key, value);
}, reporter);
Copy the code

Use of… Convert the Map structure to an array structure

const map = new Map([[1.'one'],
  [2.'two'],
  [3.'three']]); [...map.keys()]/ / [1, 2, 3]

[...map.values()]
// ['one', 'two', 'three']

[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]

[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
Copy the code

Map traversal and filtering

Combined with the map method and filter method of array, (Map itself does not have Map and filter methods)

const map0 = new Map()
  .set(1.'a')
  .set(2.'b')
  .set(3.'c');

const map1 = new Map(
  [...map0].filter(([k, v]) = > k < 3));{1 => 'a', 2 => 'b'}

const map2 = new Map(
  [...map0].map(([k, v]) = > [k * 2.'_' + v])
    );
{2 => '_A ', 4 =>' _B ', 6 => '_c'}

Copy the code

Interconversion with other data structures

Map to array, using extension operator (…)

const myMap = new Map()
  .set(true.7)
  .set({foo: 3},'abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
Copy the code

Array to Map

The array is converted to a Map by passing it to the Map constructor.

new Map([[true.7],
  [{foo: 3},'abc']]])// Map {
// true => 7,
// Object {foo: 3} => ['abc']
// }
Copy the code

Map to object

If all Map keys are strings, it can be converted to objects losslessly.

If there is a non-string key name, it is converted to a string and used as the object’s key name.

function strMapToObj(strMap) {
  let obj = Object.create(null);
  for (let [k,v] of strMap) {
    obj[k] = v;
  }
  return obj;
}

const myMap = new Map()
  .set('yes'.true)
  .set('no'.false);
strMapToObj(myMap)
// { yes: true, no: false }
Copy the code

Object to Map

function objToStrMap(obj) {
  let strMap = new Map(a);for (let k of Object.keys(obj)) {
    strMap.set(k, obj[k]);
  }
  return strMap;
}

objToStrMap({yes: true.no: false})
// Map {"yes" => true, "no" => false}
Copy the code

The Map to JSON

There are two different cases of Map conversion to JSON.

In case 1, Map keys are all strings, so you can choose to convert to JSON
function strMapToObj(strMap) {
    let obj = Object.create(null);
    for (let [k,v] of strMap) {
        obj[k] = v;
    }
    return obj;
}
function strMapToJson(strMap) {
  return JSON.stringify(strMapToObj(strMap));
}

let myMap = new Map().set('yes'.true).set('no'.false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'
Copy the code
In case 2, the Map has a non-string key name, which can be converted to an array JSON
function mapToArrayJson(map) {
  return JSON.stringify([...map]);
}

let myMap = new Map().set(true.7).set({foo: 3},'abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
Copy the code

JSON into the Map

JSON to Map. Normally, all key names are strings.

function jsonToStrMap(jsonStr) {
  return objToStrMap(JSON.parse(jsonStr));
}

jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes' => true, 'no' => false}
Copy the code

In particular, the entire JSON is an array, and each array member itself is an array with two members.

In this case, it can be converted into a Map. This is often the inverse of the Map to array JSON.

function jsonToMap(jsonStr) {
  return new Map(JSON.parse(jsonStr));
}

jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}
Copy the code

WeakMap

WeakMap structure is similar to Map structure and is also used to generate a set of key-value pairs.

Difference between WeakMap and Map

WeakMap only accepts objects as key names (except null)

const map = new WeakMap(a); map.set(1.2)
// TypeError: 1 is not an object!
map.set(Symbol(), 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 pointed to by the key name of WeakMap is not included in the garbage collection mechanism

WeakMap is designed so that sometimes we want to put some data on an object, but that creates a reference to that object.

WeakMap weakly references only the key name, not the key value. Key values are still normal references.

// Raw array storage
const e1 = document.getElementById('foo');
const e2 = document.getElementById('bar');
const arr = [
  [e1, 'foo elements'],
  [e2, 'the bar elements']];Copy the code

E1 and e2 are two objects in the above code. Add some text to these two objects through the ARR array. This forms arR’s reference to E1 and E2.

Once these two objects are no longer needed, we must manually remove the reference, otherwise the garbage collection mechanism will not free the memory occupied by E1 and E2.


WeakMap can be used to add data to objects without interfering with garbage collection

/ / WeakMap storage
const wm = new WeakMap(a);const element = document.getElementById('example');  // Number of references 1

wm.set(element, 'some information');	// The number of references is still 1
wm.get(element) // "some information"
Copy the code

The DOM node object above has a reference count of 1, not 2. At this point, once the reference to the node is removed (Element = NULL), its memory footprint is freed by the garbage collection mechanism.


WeakMap weakly references only the key name, not the key value. Key values are still normal references.

const wm = new WeakMap(a);let key = {};
let obj = {foo: 1};

wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}
// the key obj is a normal reference.
Copy the code

The grammar of the WeakMap

There are two main differences between WeakMap and Map in API:

First, there is no traversal operation and no size attribute

Second, it is impossible to empty

WeakMap has only four methods available:

get()
set()
has()
delete()

The purpose of the WeakMap

The typical case is DOM nodes as key names

let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap(a); myWeakmap.set(myElement, {timesClicked: 0});

myElement.addEventListener('click'.function() {
  let logoData = myWeakmap.get(myElement);
  logoData.timesClicked++;
}, false);
Copy the code

MyElement is a DOM node whose state is updated every time a click event occurs. We put this state in WeakMap as a key value, and the corresponding key name is myElement. Once the DOM node is removed, the state disappears automatically, with no risk of memory leaks.

2. Deploy private properties

const _counter = new WeakMap(a);const _action = new WeakMap(a);class Countdown {
  constructor(counter, action) {
    _counter.set(this, counter);
    _action.set(this, action);
  }
  dec() {
    let counter = _counter.get(this);
    if (counter < 1) return;
    counter--;
    _counter.set(this, counter);
    if (counter === 0) {
      _action.get(this) (); }}}const c = new Countdown(2.() = > console.log('DONE'));

c.dec()
c.dec()
// DONE
Copy the code