Review
- 2019-03-09
- 2020-03-26
- 2024-07-24 21:32
[!本文摘要] Keyed collections – namely,
Map,Set,WeakMap, andWeakSet1:Map– is a collection of keyed values. 2:Set– is a collection of unique values. 3:WeakMapisMap-like collection that allows only objects as keys and removes them together with associated value once they become inaccessible by other means.weak reference4:WeakSetisSet-like collection that stores only objects and removes them once they become inaccessible by other means.weak reference5:Set比ArraycheckExist(has), 删除(delete)更快 These newer keyed collections are more flexible, easier to iterate over, and higher-performing.
一、Introduction #
Keyed collections are data collections that are ordered by key not index. They are associative in nature. Map and set objects are keyed collections and are iterable in the order of insertion.
Map #
A
Map object is a simple key/value map and can iterate its elements in insertion order.
Features
- It only contains what is explicitly put into it
- Keys can be any value
- The keys are ordered
- A Map is an iterable, so it can be directly iterated
- Performs better in scenarios involving frequent additions and removals of key-value paris
const sayings = new Map();
// const sayings = new Map([[true, 'ok']]);
// const sayings = new Map(Object.entries(obj));
sayings.set("dog", "woof");
// Every `map.set` call returns the map itself, so we can “chain” the calls:
sayings.set("cat", "meow").set("elephant", "toot");
sayings.size; // 3
sayings.get("dog"); // woof
sayings.get("fox"); // undefined
sayings.has("bird"); // false
sayings.delete("dog"); // `true` if an element in the `Map` object existed and has been removed, or `false` if the element does not exist.
sayings.has("dog"); // false
for (const [key, value] of sayings) {
console.log(`${key} goes ${value}`);
}
// "cat goes meow"
// "elephant goes toot"
sayings.clear(); // return undefined
sayings.size; // 0
Object vs Map
- The keys of an
Objectare strings or symbols, whereas they can be of any value for aMap. - You can get the
sizeof aMapeasily, while you have to manually keep track of size for anObject. - The iteration of
mapsis in insertion order of the elements. - An
Objecthas a prototype, so there are default keys in the map. (This can be bypassed usingmap = Object.create(null).)
let john = { name: "John" };
let ben = { name: "Ben" };
let visitsCountObj = {}; // try to use an object
visitsCountObj[ben] = 234; // try to use ben object as the key
visitsCountObj[john] = 123; // try to use john object as the key, ben object will get replaced
// That's what got written!
alert( visitsCountObj["[object Object]"] ); // 123
whether to use a Map or an Object:
- Use maps over objects when keys are unknown until run time, and when all keys are the same type and all values are the same type.
- Use maps if there is a need to store primitive values as keys because object treats each key as a string whether it’s a number value, boolean value or any other primitive value.
- Use objects when there is logic that operates on individual elements.
Besides that, Map has a built-in forEach method, similar to Array:
// runs the function for each (key, value) pair
recipeMap.forEach( (value, key, map) => {
alert(`${key}: ${value}`); // cucumber: 500 etc
});There’s Object.fromEntries method that does the reverse: given an array of [key, value] pairs, it creates an object from them:
let prices = Object.fromEntries([
['banana', 1],
['orange', 2],
['meat', 4]
]);
// now prices = { banana: 1, orange: 2, meat: 4 }
alert(prices.orange); // 2
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);
let obj = Object.fromEntries(map.entries()); // make a plain object (*)
// done!
// obj = { banana: 1, orange: 2, meat: 4 }
alert(obj.orange); // 2
const first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const second = new Map([
[1, "uno"],
[2, "dos"],
]);
// Merge two maps. The last repeated key wins.
// Spread syntax essentially converts a Map to an Array
const merged = new Map([...first, ...second]);
console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
WeakMap #
A
WeakMap is a collection of key/value pairs whose keys must be objects or
non-registered symbols, with values of any arbitrary
JavaScript type, and which does not create strong references to its keys. That is, an object’s presence as a key in a WeakMap does not prevent the object from being garbage collected.
Use case
- additional data storage
- caching
- Store private data for an object, or to hide implementation details.
let visitsCountMap = new WeakMap(); // weakmap: user => visits count
// increase the visits count
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
let john = { name: "John" };
countUser(john); // count his visits
// later john leaves us
john = null;// 📁 cache.js
let cache = new WeakMap();
// calculate and remember the result
function process(obj) {
if (!cache.has(obj)) {
let result = /* calculate the result for */ obj;
cache.set(obj, result);
return result;
}
return cache.get(obj);
}
// 📁 main.js
let obj = {/* some object */};
let result1 = process(obj);
let result2 = process(obj);
// ...later, when the object is not needed any more:
obj = null;
// Can't get cache.size, as it's a WeakMap,
// but it's 0 or soon be 0
// When obj gets garbage collected, cached data will be removed as well
const privates = new WeakMap();
function Public() {
const me = {
// Private data goes here
};
privates.set(this, me);
}
Public.prototype.method = function () {
const me = privates.get(this);
// Do stuff with private data in `me`
// …
};
module.exports = Public;Now, if we use an object as the key in it, and there are no other references to that object – it will be removed from memory (and from the map) automatically.
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // overwrite the reference
// john is removed from memory!
WeakMap has only the following methods:
weakMap.set(key, value)weakMap.get(key)weakMap.delete(key)weakMap.has(key)
Set #
Set objects are collections of unique values. You can iterate its elements in insertion order. A value in a Set may only occur once; it is unique in the Set’s collection.
const mySet = new Set();
mySet.add(1);
mySet.add("some text");
mySet.add("foo"); // adds a value, returns the set itself.
mySet.has(1); // true
mySet.delete("foo"); // removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`.
mySet.size; // 2
for (const item of mySet) {
console.log(item);
}
// 1
// "some text"
mySet.clear(); // removes everything from the set.
Array.from(mySet);
[...mySet2];
mySet2 = new Set([1, 2, 3, 4]);Array vs Set
- Deleting
Arrayelements by value (arr.splice(arr.indexOf(val), 1)) is very slow. Setobjects let you delete elements by their value. With an array, you would have tosplicebased on an element’s index.- The value
NaNcannot be found withindexOfin an array. Setobjects store unique values. You don’t have to manually keep track of duplicates.
The alternative to
Setcould be anarrayof users, and the code to check for duplicates on every insertion using arr.find. But the performance would be much worse, because this method walks through the whole array checking every element.Setis much better optimized internally for uniqueness checks.
let set = new Set(["oranges", "apples", "bananas"]);
for (let value of set) alert(value);
// the same with forEach:
set.forEach((value, valueAgain, set) => {
alert(value);
});WeakSet #
WeakSet objects are collections of garbage-collectable values, including objects and
non-registered symbols. A value in the WeakSet may only occur once. It is unique in the WeakSet’s collection.
Set vs WeakSet
- In contrast to
Sets,WeakSetsare collections of objects or symbols only, and not of arbitrary values of any type. - The
WeakSetis weak: References to objects in the collection are held weakly. If there is no other reference to an object stored in theWeakSet, they can be garbage collected. That also means that there is no list of current objects stored in the collection. WeakSetsare not enumerable.
The use cases of
WeakSetobjects are limited. They will not leak memory, so it can be safe to use DOM elements as a key and mark them for tracking purposes.
methods
addhasdelete
Reference #
Keyed collections ES6 keyed collections: Maps and sets How to Use JavaScript Collections – Map and Set Map and Set WeakMap and WeakSet