WeakMap and WeakSet — what are they and when do you use them
Map/Set variants that hold their keys/values weakly — if nothing else references the object, it can be garbage-collected and the entry disappears. Keys must be objects; not iterable; no size. Use for: per-object private data, object-keyed caches that self-clean, and marking objects without leaking memory.
WeakMap and WeakSet are like Map/Set but with weakly-held keys/values — which changes both their behavior and their use cases.
What "weak" means
A normal Map keeps a strong reference to its keys — as long as the Map lives, those key objects can't be garbage-collected, even if nothing else references them. Use objects as keys and forget to delete, and you've leaked memory.
A WeakMap holds keys weakly: if the WeakMap entry is the only thing referencing a key object, that object is eligible for garbage collection, and when it's collected the entry vanishes automatically. WeakSet does this for its values.
The constraints (they follow from being weak)
- Keys/values must be objects (or non-registered symbols) — not primitives, which have no identity to weakly track.
- Not iterable, no
.size, no.clear()— because GC timing is non-deterministic, the contents aren't observably stable, so enumeration isn't allowed. - Minimal API — WeakMap:
get/set/has/delete; WeakSet:add/has/delete.
When to use them
1. Private data tied to an object's lifetime:
const _private = new WeakMap();
class User {
constructor(name) { _private.set(this, { name }); }
getName() { return _private.get(this).name; }
}When a User is GC'd, its private data goes too — no leak, no manual cleanup.
2. Caching / memoization keyed by object:
const cache = new WeakMap();
function getLayout(node) {
if (cache.has(node)) return cache.get(node);
const layout = expensiveMeasure(node);
cache.set(node, layout);
return layout;
}A normal Map here would pin every node forever; the WeakMap lets them be collected.
3. Marking / tracking objects — WeakSet for "have I already visited/processed this object?" without keeping it alive.
4. Metadata for DOM nodes — attach data to elements; it's freed when the element is removed and GC'd.
The mental model
Use them when you want data associated with an object for exactly as long as that object lives — the GC handles cleanup for you.
The framing
"They're Map/Set whose keys/values are held weakly — if nothing else references the object, it gets garbage-collected and the entry disappears on its own, so you can't leak memory by forgetting to delete. That weakness forces the constraints: keys must be objects, and they're not iterable with no size, because GC timing is non-deterministic. I reach for them to tie data to an object's lifetime — per-instance private state, object-keyed caches that self-clean, marking objects as seen, DOM-node metadata."
Follow-up questions
- •Why can't you iterate a WeakMap or get its size?
- •Why must keys be objects?
- •How would a normal Map leak memory where a WeakMap doesn't?
- •How do you implement private fields with a WeakMap?
Common mistakes
- •Trying to use primitives as keys.
- •Expecting iteration or .size.
- •Using a regular Map for object-keyed caches and leaking memory.
- •Relying on observing exactly when entries are removed.
Performance considerations
- •Their whole point is memory: they let the GC reclaim objects and associated data instead of pinning them — preventing the slow memory growth that object-keyed regular Maps cause in long-lived apps.
Edge cases
- •Removal timing is unobservable — don't depend on it.
- •Key still referenced elsewhere — not collected.
- •Non-registered symbols now allowed as keys.
Real-world examples
- •Libraries storing private instance state in a WeakMap.
- •Self-cleaning memoization caches keyed by object; DOM-node metadata.