ES2015 (ES6) added several new data structures. While Map and Set are more straightforward, ES2015 also added “weak collections” like WeakMap and WeakSet.
In this post we’ll go over why maps were added, what’s “weak” about a WeakMap and when one might want to use it.
What we did before maps
Bracket notation allows us to access a key by “dynamic” value which enables this:
What went wrong?
This also means that we can’t store object keys meaningfully:
Maps let us store non-string keys in objects.
A map is created with the Map constructor and the properties of Map can be anything:
This means that maps take object keys, unlike objects. Sadly, we can’t define the hash function in a built-in map – but we might one day.
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:
Now, I have a method that uses the object:
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:
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:
And the memory leak is gone.
Some use cases that would otherwise cause a memory leak and are enabled by WeakMaps include:
- Keeping private data about a specific object and only giving access to it to people with a reference to the Map. A more ad-hoc approach is coming with the private-symbols proposal but that’s a long time from now.
- Keeping data about library objects without changing them or incurring overhead.
- Keeping data about a small set of objects where many objects of the type exist to not incur problems with hidden classes JS engines use for objects of the same type.
- Keeping data about host objects like DOM nodes in the browser.
- Adding a capability to an object from the outside (like the event emitter example in the other answer).
Should I use a WeakMap?
In general, I would recommend using a WeakMap only after considering other alternatives since they are inherently less visible than regular maps and objects (for example, they can’t be iterated).
However, when you extend an object from the outside (which isn’t uncommon) – it’s worth considering.
Author bio: This post was written by Benjamin Gruenbaum. Benji is an open source lover, Node.js core collaborator, core team at Bluebird, Sinon, MobX and other open-source libraries.