What are the actual uses of the WeakMap
data structure introduced in ECMAScript 6?
Since a key of a weak map creates a strong reference to its corresponding value, ensuring that a value which has been inserted into a weak map will never disappear as long as its key is still alive, it can't be used for memo tables, caches or anything else that you would normally use weak references, maps with weak values, etc. for.
It seems to me that this:
weakmap.set(key, value);
...is just a roundabout way of saying this:
key.value = value;
What concrete use cases am I missing?
WeakMap allows associating data to objects in a way that doesn't prevent the key objects from being collected, even if the values reference the keys.
add() method. WeakSet in JavaScript is used to store a collection of objects. It adapts the same properties of that of a set i.e. does not store duplicates. The major difference of a WeakSet with the set is that a WeakSet is a collection of objects and not values of some particular type.
WeakMaps provide a way to extend objects from the outside without interfering with garbage collection. Whenever you want to extend an object but can't because it is sealed - or from an external source - a WeakMap can be applied.
A WeakMap is a map (dictionary) where the keys are weak - that is, if all references to the key are lost and there are no more references to the value - the value can be garbage collected. Let's show this first through examples, then explain it a bit and finally finish with real use.
Let's say I'm using an API that gives me a certain object:
var obj = getObjectFromLibrary();
Now, I have a method that uses the object:
function useObj(obj){ doSomethingWith(obj); }
I want to keep track of how many times the method was called with a certain object and report if it happens more than N times. Naively one would think to use a Map:
var map = new Map(); // maps can have object keys function useObj(obj){ doSomethingWith(obj); var called = map.get(obj) || 0; called++; // called one more time if(called > 10) report(); // Report called more than 10 times map.set(obj, called); }
This works, but it has a memory leak - we now keep track of every single library object passed to the function which keeps the library objects from ever being garbage collected. Instead - we can use a WeakMap
:
var map = new WeakMap(); // create a weak map function useObj(obj){ doSomethingWith(obj); var called = map.get(obj) || 0; called++; // called one more time if(called > 10) report(); // Report called more than 10 times map.set(obj, called); }
And the memory leak is gone.
Some use cases that would otherwise cause a memory leak and are enabled by WeakMap
s include:
It can be used to extend an object from the outside. Let's give a practical (adapted, sort of real - to make a point) example from the real world of Node.js.
Let's say you're Node.js and you have Promise
objects - now you want to keep track of all the currently rejected promises - however, you do not want to keep them from being garbage collected in case no references exist to them.
Now, you don't want to add properties to native objects for obvious reasons - so you're stuck. If you keep references to the promises you're causing a memory leak since no garbage collection can happen. If you don't keep references then you can't save additional information about individual promises. Any scheme that involves saving the ID of a promise inherently means you need a reference to it.
WeakMaps mean that the keys are weak. There are no ways to enumerate a weak map or to get all its values. In a weak map, you can store the data based on a key and when the key gets garbage collected so do the values.
This means that given a promise you can store state about it - and that object can still be garbage collected. Later on, if you get a reference to an object you can check if you have any state relating to it and report it.
This was used to implement unhandled rejection hooks by Petka Antonov as this:
process.on('unhandledRejection', function(reason, p) { console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason); // application specific logging, throwing an error, or other logic here });
We keep information about promises in a map and can know when a rejected promise was handled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With