WeakMap and WeakSet in JavaScript
WeakMap and WeakSet are special collection types in JavaScript that hold objects weakly. This means the garbage collector can clean up entries when those objects are no longer referenced elsewhere in your program. They exist to solve specific problems around memory management and object association.
What Makes WeakMap and WeakSet Different?
Regular Maps and Sets hold strong references to their keys. An object used as a Map key won’t be garbage collected as long as the Map exists, even if nothing else references it. WeakMap and WeakSet solve this by holding weak references.
The trade-off: you cannot iterate over WeakMap or WeakSet, and you cannot check their size. This limitation is intentional — it ensures the garbage collector can do its job without interference.
const weakMap = new WeakMap();
const obj = { name: "temporary" };
weakMap.set(obj, "some value");
// When obj has no other references, the entry
// can be garbage collected automatically
obj = null;
// The weakMap entry may disappear at any time
WeakMap in Practice
WeakMap is ideal for associating data with objects without preventing those objects from being collected. This is useful for:
- Private data storage
- Caching with object keys
- DOM node metadata
Private Data Pattern
WeakMap provides a clean way to attach private data to objects:
const privateData = new WeakMap();
class User {
constructor(name, email) {
// Store private data in the WeakMap
privateData.set(this, { name, email });
}
getName() {
return privateData.get(this).name;
}
getEmail() {
return privateData.get(this).email;
}
}
const user = new User("Alice", "alice@example.com");
console.log(user.getName()); // "Alice"
console.log(user.getEmail()); // "alice@example.com"
The privateData WeakMap doesn’t prevent the User object from being garbage collected. When user goes out of scope, both the object and its private data disappear together.
DOM Node Metadata
WeakMap shines for attaching metadata to DOM nodes:
const elementData = new WeakMap();
function trackElement(element) {
const data = {
clicks: 0,
views: 0,
lastInteraction: Date.now()
};
elementData.set(element, data);
element.addEventListener('click', () => {
data.clicks++;
data.lastInteraction = Date.now();
});
return data;
}
// Each element tracks its own metadata
const button = document.querySelector('#myButton');
const stats = trackElement(button);
// When the button is removed from DOM and no longer
// referenced, the WeakMap entry cleans itself up
Caching with WeakMap
Use WeakMap for caching when you want automatic cleanup:
const cache = new WeakMap();
function expensiveOperation(obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
// Simulate expensive computation
const result = computeExpensiveValue(obj);
cache.set(obj, result);
return result;
}
function computeExpensiveValue(obj) {
// ... expensive computation
return { processed: true, input: obj.id };
}
When the cached objects are no longer referenced elsewhere, the cache entries vanish automatically.
WeakSet in Practice
WeakSet holds a collection of objects. Like WeakMap, entries are garbage collected when the objects have no other references.
WeakSet is useful when you need to mark objects without preventing their collection:
const markedObjects = new WeakSet();
function markAsProcessed(obj) {
markedObjects.add(obj);
return obj;
}
function isProcessed(obj) {
return markedObjects.has(obj);
}
// Usage
const data = { id: 1, values: [1, 2, 3] };
markAsProcessed(data);
console.log(isProcessed(data)); // true
// When data is no longer referenced, it's removed
// from markedObjects automatically
Tracking Enabled Objects
A practical pattern is tracking which objects should receive special treatment:
const enabledNodes = new WeakSet();
function enableNode(node) {
enabledNodes.add(node);
node.classList.add('enabled');
}
function disableNode(node) {
enabledNodes.delete(node);
node.classList.remove('enabled');
}
function toggleNode(node) {
if (enabledNodes.has(node)) {
disableNode(node);
} else {
enableNode(node);
}
}
What You Cannot Do
WeakMap and WeakSet have intentional limitations:
const weakMap = new WeakMap();
// These operations DO NOT work:
weakMap.size // undefined
weakMap.keys() // TypeError
weakMap.values() // TypeError
weakMap.entries() // TypeError
weakMap.forEach() // TypeError
for (const key of weakMap) { } // TypeError
The same applies to WeakSet — no iteration, no size, no keys/values/entries.
This is by design. If you could iterate, you’d hold strong references to keys, defeating the purpose.
When to Use Each
Choose the right collection for your use case:
| Scenario | Use Map | Use WeakMap |
|---|---|---|
| Keys are primitives | Yes | No — only objects |
| Need to iterate keys | Yes | No |
| Need size | Yes | No |
| Keys should be auto-cleaned | No | Yes |
| Memory-sensitive caching | No | Yes |
For WeakSet vs Set, the logic is similar — use WeakSet when you want automatic cleanup and don’t need iteration.
Garbage Collection Timing
The JavaScript engine decides when to collect entries from WeakMap and WeakSet. You cannot predict or control exactly when cleanup happens:
const weakMap = new WeakMap();
const obj = { data: "important" };
weakMap.set(obj, "value");
// Force a garbage collection (if supported)
if (globalThis.gc) {
globalThis.gc();
}
// obj may or may not be collected at this point
// depending on the engine's implementation
In Node.js, you can force garbage collection with --expose-gc flag and calling globalThis.gc().
Common Misconceptions
Misconception: WeakMap values are weakly held. Only keys are weakly held. Values are regular references. If you hold a strong reference to a value elsewhere, it won’t be collected even if the key is collected.
Misconception: WeakMap is for security. WeakMap provides privacy through obscurity, not true encapsulation. Determined attackers can access WeakMap contents through debugging tools.
Misconception: WeakMap replaces Map entirely. They solve different problems. Use Map when you need iteration or size. Use WeakMap for memory-sensitive object associations.
See Also
- Object Methods — Related static methods on the Object constructor
- Map and Set — Standard Map and Set collections for comparison
- JavaScript Memory Management — Deep dive into how JavaScript handles memory