JavaScript (ES-2015) Set, Map, WeakSet and WeakMap
Bài đăng này đã không được cập nhật trong 7 năm
In ES-2015, new types of collections have appeared in JavaScript: Set, Map, WeakSet and WeakMap.
Map
Map is a collection for storing records like key: value
.
Unlike objects in which keys can only be strings, the Map
key can have an arbitrary value, for example:
'use strict';
let map = new Map();
map.set('1', 'str1'); // key-string
map.set(1, 'num1'); // number
map.set(true, 'bool1'); // boolean value
// In an ordinary object this would be the same thing,
// map saved type of key
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'
alert( map.size ); // 3
As you can see from the example above, get
and set
methods are used to store and read values. Both keys and values are stored "as is", without type conversions.
The map.size
property stores the total number of records in map.
The set
method can be:
map
.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
When you create a Map
, you can immediately initialize it with a list of values.
A map
object with three keys, as in the example above:
let map = new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]);
The argument to new Map
must be an iterated object (not necessarily an array). Everywhere duck typing, maximum flexibility.
You can also use objects as map keys:
'use strict';
let user = { name: "Jamie" };
// For each user we will store the number of visits
let visitsCountMap = new Map();
// Object user is the key in в visitsCountMap
visitsCountMap.set(user, 123);
alert( visitsCountMap.get(user) ); // 123
Using objects as keys is just the case when Map is difficult to replace with ordinary Object
objects. After all, for ordinary objects, the key can only be a string.
How map compares keys?
To check the values for equivalence, use the SameValueZero algorithm. It is similar to strict equality ===
, the difference is that NaN
is considered equal to NaN
. Therefore, the NaN
value can also be used as a key.
This algorithm can not be changed or set its own comparison function.
Methods for deleting entries:
Map.delete (key)
deletes the entry with thekey
key, returns true if such an entry was, otherwisefalse
.Map.clear ()
- deletes all records, clearsmap
.
To verify the existence of the key:
map.has(key)
– returnstrue
, if key, elsefalse
.
Iteration
To iterate through the map
, one of three methods is used:
Map.keys ()
- returns the iterated object for the keys,Map.values ()
- returns the iterated object for values,Map.entries ()
- returns the iterated object for records[key, value]
, it is used by default infor..of
.
For example:
'use strict';
let recipeMap = new Map([
['Cucumber', '500 gr'],
['Tomatoes', '350 gr'],
['Sour cream', '50 gr']
]);
// loop by keys
for(let fruit of recipeMap.keys()) {
alert(fruit); // Cucumber, Tomatoes, Sour cream
}
// loop by values [key, value]
for(let amount of recipeMap.values()) {
alert(amount); // 500 gr, 350 gr, 50 gr
}
// loop by recoeds
for(let entry of recipeMap) { // same like recipeMap.entries()
alert(entry); // Cucumber, 500 gr , etc., arrays by 2 value
}
The search goes in the same order as the insertion
The search is performed in the order of insertion. Map
objects guarantee this, unlike the usual Object
objects.
In addition, Map
has a standard forEach
method, similar to an array:
'use strict';
let recipeMap = new Map([
['Cucumber', '500 gr'],
['Tomatoes', '350 gr'],
['Sour cream', '50 gr']
]);
recipeMap.forEach( (value, key, map) => {
alert(`${key}: ${value}`); // Cucumber: 500 gr, etc.
});
Set
Set
is a collection for storing a set of values, and each value can occur only once.
For example, visitors come to us, and we would like to save everyone who came. In this case, repeated visits should not lead to duplicates, that is, each visitor needs to "count" exactly once.
Set
for this perfectly suits:
'use strict';
let set = new Set();
let jamie = {name: "Jamie"};
let josh = {name: "Josh"};
let julie = {name: "Julie"};
// Visits, some users go many times
set.add(jamie);
set.add(josh);
set.add(julie);
set.add(jamie);
set.add(josh);
// set stores only unique values
alert( set.size ); // 3
set.forEach( user => alert(user.name ) ); // Jamie, Josh, Julie
In the example above, multiple additions of the same object to set
do not create unnecessary copies.
Alternative Set
are arrays with a duplicate search for each addition, but they are much worse in performance. Or you can use ordinary objects, where the key is some unique identifier of the visitor. But this is less convenient than a simple and intuitive Set
.
Basic methods:
Set.add (item)
- adds anitem
to the collection, returns set.Set.delete (item)
- removesitem
from the collection, returnstrue
if it was there, otherwisefalse
.Set.has (item)
- returnstrue
ifitem
is in the collection, otherwisefalse
.Set.clear ()
- clears theset
.
The Set
is enumerated through forEach
or for..of
similarly to Map
:
'use strict';
let set = new Set(["orange", "apple", "banana"]);
// same: for(let value of set)
set.forEach((value, valueAgain, set) => {
alert(value); // orange, then apple, then banana
});
Note that in Set
, the function in .forEach
has three arguments: value, once again the value, and then the set itself to be sorted. The value is repeated twice in the arguments.
This is done for compatibility with Map
, where the .forEach
function also has three arguments. But in Set the first two always match and contain the next value of the set.
WeakMap and WeakSet
WeakSet
is a special kind of Set that does not prevent the garbage collector from deleting its elements. The same is WeakMap
for Map
.
That is, if an object is present only in the WeakSet
/ WeakMap
- it is deleted from memory.
This is necessary for those situations where the main place for storing and using objects is somewhere else in the code place, and here we want to store "auxiliary" data for them, existing only while the object is alive.
For example, we have elements on a page or, for example, users, and we want to store auxiliary information for them, for example, event handlers or just data, but only valid as long as the object to which they relate exists.
If we place such data in the WeakMap
, and the object is made a key, then they will be automatically deleted from memory when the element is deleted.
For example:
// Current active users
let activeUsers = [
{name: "Jamie"},
{name: "Josh"},
{name: "Jolie"}
];
// Auxiliary information about them,
// Which is not directly included in the user object,
// And therefore stored separately
let weakMap = new WeakMap();
weakMap[activeUsers[0]] = 1;
weakMap[activeUsers[1]] = 2;
weakMap[activeUsers[2]] = 3;
alert( weakMap[activeUsers[0]] ); // 1
activeUsers.splice(0, 1); // Jamie is no longer active user
// weakMap Now contains only 2 items
activeUsers.splice(0, 1); // Josh is no longer active user
// weakMap Now contains only 1 item
Thus, WeakMap
eliminates the need to manually delete auxiliary data when the main object is deleted.
WeakMap has a number of limitations:
- There is no
size
property. - You can not enumerate elements with an iterator or
forEach
. - There is no
clear ()
method.
In other words, WeakMap
only works on writing (set, delete) and reading (get, has) elements for a particular key, and not as a full collection. You can not display all the contents of the WeakMap
, there are no corresponding methods.
This is due to the fact that the contents of the WeakMap
can be modified by the garbage collector at any time, regardless of the programmer. The garbage collector works by itself. It does not guarantee that it will clean the object immediately when it becomes possible. Equally, it does not guarantee the opposite. There is no specific moment when such clearing will occur exactly - this is determined by the internal algorithms of the collector and his information about the system.
Therefore, the content of WeakMap
at an arbitrary moment, strictly speaking, is not defined. Maybe the garbage collector has already deleted some records, or maybe not. With this, as well as with the requirements for effective implementation of WeakMap
, there is a lack of methods that access all records.
The same applies to WeakSet
: you can add items, check their availability, but you can not get their list and even find out the quantity.
These restrictions may seem inconvenient, but in fact they do not prevent WeakMap / WeakSet from fulfilling its main task - to be a "secondary" data repository for objects whose current list (and themselves) is stored elsewhere.
Total
Map
- a collection of records likekey: value
, betterObject
in that it always sorts in the insert order and allows any keys.Set
- a collection of unique elements, also allows for any keys.
The main application of Map
is the situation when there are not enough string keys (you need to store the correspondences for object keys), or when the string key can be completely arbitrary.
For example, in an ordinary Object, you can not use "completely any" keys. There are built-in methods, and certainly there is a property named __proto__
, which is reserved by the system. If the key name is given by the site visitor, then it can try to use this property, replace the prototype, and this, when running JavaScript on the server, can already lead to serious errors.
WeakMap
andWeakSet
are the "cut-down" options forMap
/Set
, which allow only "point-by-point" access to elements (for a particular key or value). They do not prevent garbage collection, that is, if the link to the object remains only inWeakSet
/WeakMap
- it will be deleted.
All rights reserved