Understanding WeakMap and WeakSet in JavaScript

Modern JavaScript offers us plenty of data structures that can help simplify common programming tasks. Amongst these, WeakMap and WeakSet stand out because of their unique approach to handling memory. At first glance, they might seem confusing or unnecessary, but they're actually extremely useful, especially when managing memory or storing metadata.
Today, I intend to explain what WeakMap and WeakSet are, demonstrate some practical ways to use them, and help you understand when each one can benefit your JavaScript applications.
Understanding JavaScript's WeakMap
A WeakMap is a special type of map, with two key differences:
- Keys can be objects or non‑registered symbols; other primitive values are not allowed.
- It doesn't prevent JavaScript from garbage‑collecting these keys when they're no longer used elsewhere.
A WeakMap does not keep an otherwise unreachable key alive. Once the key is collected, the entry no longer keeps its associated value alive either, though that value may still be referenced elsewhere. Garbage collection has no promised schedule.
A Practical Example of WeakMap
A common use is attaching metadata to DOM elements without retaining them solely through a lookup table. Here is a TypeScript example:
const buttonData = new WeakMap<Element, { clicked: boolean }>();
const button = document.getElementById('submitBtn');
button?.addEventListener('click', () => {
buttonData.set(button, { clicked: true });
});
// DOM removal alone does not remove strong references such as button.
// The WeakMap does not keep an otherwise unreachable element alive.Removing the button from the DOM is not enough on its own. The button variable or other application code may still hold a strong reference to it. The weak entry becomes useful when those other references are gone.
Understanding JavaScript's WeakSet
A WeakSet is similar to a regular JavaScript Set, but with two key differences:
- It stores objects or non‑registered symbols, but not other primitive values.
- Like
WeakMap, it doesn't prevent objects from being garbage‑collected.
This makes it ideal when you want to keep track of objects without explicitly worrying about removing them later.
A Practical Example of WeakSet
A typical use of WeakSet is tracking objects you've already processed without attaching additional information, for example:
const visitedNodes = new WeakSet<object>();
function traverseNode(node: object) {
if (visitedNodes.has(node)) return;
visitedNodes.add(node);
// Process the node here
}The WeakSet does not keep node alive after other strong references are gone. That permits collection; it does not tell us when collection will happen.
When to Choose WeakMap vs. WeakSet
Choosing between these two structures depends on your needs:
- Use
WeakMapwhen you need to store extra data associated with objects that can be safely cleaned up when objects are no longer used. - Use
WeakSetwhen you just need to keep track of objects you've encountered without associating extra data.
Here's another way of simplifying your choice:
- Storing data about objects? Choose
WeakMap. - Tracking unique objects without extra data? Go with
WeakSet.
How Are They Different from Regular Maps and Sets?
A reachable Map or Set strongly retains its entries until they are removed. A weak collection avoids keeping its keys or members alive on its own. Regular collections can also be collected when the collection itself becomes unreachable.
Also:
- You can iterate over regular
Maps andSets, but not overWeakMaps andWeakSets. WeakMapkeys andWeakSetmembers must be objects or non‑registered symbols. AWeakMapvalue can be any JavaScript value, including a string, number,nullorundefined.
Practical Usage: Encapsulating Data with WeakMap
Another useful application for WeakMap is encapsulating instance data behind module or closure scope, like this:
const privateData = new WeakMap<object, { secret: string }>();
class SecretHolder {
constructor(secret: string) {
privateData.set(this, { secret: secret });
}
reveal() {
return privateData.get(this)?.secret;
}
}
const mySecret = new SecretHolder('WeakMaps are handy!');
console.log(mySecret.reveal()); // WeakMaps are handy!
console.log(privateData.has(mySecret)); // true within this module scopeThis pattern keeps the data away from consumers that cannot access privateData. Code in the same module or closure scope can still read the WeakMap, so this is scope‑based encapsulation rather than a language private field such as #secret.
Does Using Weak Collections Improve Performance?
The specific benefit is avoiding a strong reference from the collection to its keys or members. That can help with metadata whose lifetime should follow another object. It does not make weak collections a general cure for memory leaks.
Choose them for that ownership behaviour, then profile if runtime or memory use matters. They do not promise faster lookups or a particular amount of memory saved.
Wrapping Up
WeakMap and WeakSet are useful when a lookup should not keep an object alive. Use WeakMap for associated data and WeakSet for membership, and keep ordinary strong collections when you need to enumerate their contents.
Key Takeaways
- Weak collections permit garbage collection when no other strong references keep their keys or members alive; collection timing is unspecified.
- Use
WeakMapfor storing metadata or extra details about objects. - Choose
WeakSetwhen you just want to keep track of unique objects. - They address one source of unwanted retention, rather than preventing every memory leak.
We still need to understand the references our application keeps. A weak lookup is helpful when its lifetime matches the objects we are tracking.