What are WeakMap and WeakSet used for in JavaScript?
Advertisement
🧩 Scenario
Architecture Walkthrough
Strong vs Weak References
A regular reference (the kind you get when you assign an object to a variable or store it in a Map key) is a strong reference. The garbage collector will not collect an object as long as any strong reference to it exists. A weak reference does not count toward reachability. An object with only weak references pointing to it is treated by the GC as unreachable and eligible for collection.
WeakMap and WeakSet hold weak references to their keys and members respectively. If the only remaining reference to an object is its role as a WeakMap key, the GC can collect the object and the WeakMap entry disappears automatically.
WeakMap: Object-Keyed Metadata
WeakMap accepts only objects as keys (not primitives). Values can be anything. Its primary use case is associating additional data with an object without controlling that object's lifecycle. A library that needs to attach state to a user-provided DOM node can use a WeakMap with the node as the key. When the node is removed from the document and all other references to it are dropped, the GC collects the node and the WeakMap entry evaporates without any manual cleanup.
WeakMap does not expose .size, .keys(), .values(), .entries(), or forEach(). These operations are intentionally absent because the entries can disappear at any GC cycle. An enumeration that might return different results every time the GC runs would be non-deterministic and misleading.
WeakSet: GC-Friendly Membership Tracking
WeakSet stores a collection of objects with the same GC-friendly property: membership does not prevent the object from being collected. Its API is minimal: add, has, delete. Like WeakMap, it has no size and no iteration. The canonical use case is tracking which objects have been visited or processed without keeping those objects alive just for the sake of the tracking set.
Key Code Explained
// WeakMap: attach metadata to objects without holding strong references
const domCache = new WeakMap();
function processNode(node) {
if (domCache.has(node)) {
return domCache.get(node); // return cached result
}
const result = computeExpensiveLayout(node);
domCache.set(node, result);
return result;
}
// When node is removed from the DOM and all other references drop:
// - GC collects the node
// - The WeakMap entry is automatically removed
// - No manual cache.delete(node) needed
// Compare with Map: node stays alive as long as the Map exists
const strongCache = new Map();
strongCache.set(node, computeExpensiveLayout(node));
// node is retained by strongCache even after removal from the DOM
// Private data pattern using WeakMap (pre-private class fields)
const _private = new WeakMap();
class Counter {
constructor(start) {
_private.set(this, { count: start });
}
increment() {
_private.get(this).count++;
}
value() {
return _private.get(this).count;
}
}
const c = new Counter(0);
c.increment();
c.value(); // 1
// _private.get(c).count is inaccessible from outside the module
// WeakSet: track processed objects without keeping them alive
const processed = new WeakSet();
function process(request) {
if (processed.has(request)) {
throw new Error('Request already processed');
}
processed.add(request);
// handle request...
// When request is done and all references drop, GC collects it
// and processed.has(request) becomes irrelevant
}
The domCache example is the most practical demonstration. A Map with DOM node keys holds strong references that keep the nodes alive indefinitely, even after they are removed from the page. The WeakMap version requires no manual cleanup because the GC handles eviction automatically when the node is no longer reachable.
Tradeoffs
| Structure | Key type | Prevents GC of key | Iterable | Has .size |
|---|---|---|---|---|
| Map | Any value | Yes (strong ref) | Yes | Yes |
| WeakMap | Objects only | No (weak ref) | No | No |
| Set | Any value | Yes (strong ref) | Yes | Yes |
| WeakSet | Objects only | No (weak ref) | No | No |
What Interviewers Actually Check
- Whether you can explain what a weak reference is and how it differs from a strong reference
- Whether you know that
WeakMapkeys must be objects, not primitives - Whether you know why
WeakMapandWeakSetdo not support iteration or.size - Whether you can describe a real use case that demonstrates the GC benefit
- Whether you know the difference between using
MapvsWeakMapfor caching object metadata
Follow-Up Questions
- How does
WeakRefdiffer fromWeakMapin terms of how you access the weakly held value? - What is
FinalizationRegistryand how does it let you run cleanup code when a weakly held object is collected? - How do modern private class fields (
#field) compare to theWeakMapprivate data pattern? - If a
WeakMapentry disappears because the key was collected, is there any way to observe when this happened? - Can you use a
WeakMapto implement a simple memoize function? What are the limitations compared to aMap-based version?
Common Candidate Mistakes
- Trying to use a string or number as a
WeakMapkey and not understanding why it throws - Trying to call
.sizeon aWeakMapor looping over it and not knowing why these operations do not exist - Using a
Mapwith object keys in a cache that accumulates entries over the page lifetime, causing the referenced objects to never be collected - Thinking the value in a
WeakMapentry is what is weakly held when it is actually the key that is held weakly - Not knowing about
WeakRefandFinalizationRegistryas more granular weak reference tools added in ES2021
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you explain the difference between a strong reference and a weak reference?
- Can you explain why using an object as a
WeakMapkey does not prevent GC of that object? - Can you describe a real use case for
WeakMapsuch as private data or per-object caching? - Can you explain why
.sizeand iteration are not available onWeakMapandWeakSet? - Can you describe a scenario where
Mapwould be wrong andWeakMapis the correct choice?
Summary
WeakMap and WeakSet hold their keys and members with weak references, meaning those references do not count toward an object's reachability for garbage collection. When an object used as a WeakMap key has no strong references remaining, the GC can collect it and the WeakMap entry disappears automatically without any explicit cleanup.
The consequence is that WeakMap and WeakSet cannot support iteration, .size, or enumeration. The contents are non-deterministic from the GC's perspective: entries may vanish between one GC cycle and the next. The API is intentionally limited to get, set, has, and delete for WeakMap, and add, has, and delete for WeakSet.
The practical pattern for WeakMap is associating metadata or cached computation results with objects (often DOM nodes or class instances) without controlling the lifecycle of those objects. When the object is no longer needed by the rest of the application, the WeakMap entry is cleaned up for free by the GC rather than requiring manual eviction.
Why can't I iterate over WeakMap or WeakSet?
Because their entries can disappear at any time due to garbage collection, so iteration results would be non-deterministic.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement