With ES6 comes a plethora of new features and changes, one of those is Weakmaps
– essentially Weakmaps are a collection of keys and values with the main constraint being the key has to be an object. In-fact, Weakmaps are very similar to that of standard ES6 maps, with a few constraints.
Unlike using a map
which allows for arbitrarily named keys, the key in a Weakmap is an object which can either be {}
or function() {}
because functions inherit from object
.
A Weakmap
holds a weak reference (get it?) to the key inside of the map. If the object is destroyed, the garbage collector knows to come and remove the entire entry from the Weakmap
thus freeing up memory.
Still confused? Here’s an example which you can actually run in the web console inside of Firefox.
[code]
let myMap = new WeakMap();
let element1 = window;
let element2 = document.querySelector(‘body’);
// We store two objects in our Weakmap
myMap.set(element1, ‘window’);
myMap.set(element2, ‘myelement’);
// If we remove one of the elements, the element2 in this case, it will get removed from our Weakmap as well
element2.parentNode.removeChild(element2);
// Remove local reference
element2 = null;
myMap.get(element2); // Undefined
[/code]
Methods & Caveats
Unlike Map
you are not able to determine the length of items stored in a Weakmap
due to there being no size attribute. A Weakmap is actually an unordered collection of objects associated with values.
One of the biggest caveats (by design) is the lack of size, followed by the inability to use strings as key names and also the inability to iterate over a WeakMap. If you want this kind of functionality, you will want to use Map instead. Keep in mind the limitations of WeakMap when deciding to use it or not.
Supported WeakMap methods:
WeakMap.set(object, ‘value’)
Just like ES6 maps, a Weakmap has a set method which only accepts an object as a key and anything for the value. Using the earlier example, you can see we are using window
and another in-page element as the key and storing a value associated with it.
WeakMap.get(object)
Just like you would query a Map using the key name, you query for a WeakMap item by passing in the object you originally supplied to .set. So this might be an element for example and if it is found, the value will be returned.
WeakMap.has(object)
Once again, similar to Map, we can determine if our WeakMap contains this particular object within the map. Returning a boolean value of either true or false if it is found.
WeakMap.delete(object)
Using the object as the key, we can remove an item from our WeakMap using the delete method. Ensuring we pass in the original object we supplied to set. This will result in has return false and get returning undefined
if we try referencing it.
WeakMap.clear()
Does that it says on the tin, all values are removed from the WeakMap if the clear method is called on it. Useful if you want to remove everything and start from scratch.
Use-cases for Weakmaps
There are only really certain circumstances where Weakmaps will be useful. The average Javascript developer might not need to even use them, opting to use a conventional Map
instead.
What a Weakmap allows us to do is store bits of data associated with a particular object in our application and when the object is destroyed, its reference is too destroyed and memory is free’d up to be used for something else.
An element in your application can have its own state using Weakmap
which keeps track of its own data and when the element is removed, the data associated with said element is also deleted. You can probably use them in unison with data attributes for configuration and you have yourself a nice memory considerate application.
Weakmaps in action
Still not entirely sure what the benefit of Weakmaps are? Then here are a few examples to show you what can be done with them.
Private class variables
In ES6 the premise of private variables is nonexistent in the classes functionality. Using Weakmaps we can actually create private variables not accessible from outside of the class and only internally.
The below example assumes you are using modules functionality, as we are just exporting the class itself and not the constant containing the private data we are storing.
The following example not only perfectly creates private variables in our class, but WeakMaps are always garbage collected when the instance is destroyed, so memory leaks are never an issue like they are when using an array.
[code]
// Define as constant
const privateData = new WeakMap();
class MyClass {
constructor(name, age) {
privateData(this, { name: name, age: age });
}
getName() {
return privateData.get(this).name;
}
getAge() {
return privateData.get(this).age;
}
}
export default MyClass;
[/code]
Storing data on a DOM element
If you’ve used jQuery before, then you have probably used the data
method where you associated one or more pieces of information with a DOM element. Weakmaps allow us to tag DOM elements with strings or objects of data.
Perhaps a more unrealistic example of wanting to store clicks on our websites logo, we can use WeakMaps to do so.
This allows us to treat DOM objects as the objects they are complete with their own states and data independent of everything else within your application that will be garbage collected if they are removed.
[code]
let myElement = document.getElementById(‘logo’);
let myWeakmap = new WeakMap();
myWeakmap.set(myElement, {timesClicked: 0});
myElement.addEventListener(‘click’, function() {
let logoData = myWeakmap.get(myElement);
logoData.timesClicked++;
myWeakmap.set(myElement, logoData);
}, false);
[/code]
Conclusion
While on the surface it might be hard to see the benefits of using Weakmaps, I personally am glad the premise of Weakmaps made their way into Javascript finally as I will probably use them to ensure that I only associate data with an object inside of my application and keep it nice and contained. I know for a fact until we get proper support for private variables in classes, Weakmaps will be my go-to solution.
Over time more and more developers will use Weakmaps and more use-cases will be discovered for them. At the moment, there are very little documented use-cases for them out there, but as time goes on, more and more uses will be found for them.
Hi,
thanks, really nice explanation 🙂 We have used excessively jQuery´s $.data for storing information in a “dom object”. Well, i see many cases to use Weakmap´s 🙂
Great article; the only thing I would have liked to see would be more real-life use cases for weakmaps. More actual situations where I might use them… hasn’t quite clicked yet for me.
@Brendan,
No worries mate. Good feedback, I’ll update the article shortly with some examples of Weakmaps in action so people like yourself can get a new perspective on how to use them.
Thank you for stopping by and commenting.
Nice article, thank you.
*In ES6 we can deal with typical constructors like
constructor(name, age) {
privateData(this, { name, age });
}
Your first example isn’t really illustrative of weakmap behavior. Because you’ve already eliminated any reference to the object formerly referenced by “element2” and set “element2” to null, it’s not surprising that attempting to fetch a value via the null returns undefined. The example doesn’t really demonstrate that the entry has been garbage-collected from the map; you *can’t* demonstrate that, and that’s kind-of the whole point. (The example is further muddled by the fact that the object “element2” referred to was a DOM node, which is certainly not garbage-collected so long as the DOM isn’t changed.)
Thanks! Very helpful.
The first code snippet doesn’t seem to be valid, because you’re essentially saying “map.get(null)” because, you’ve set the variable to null. So of course it’s going to return undefined.