WeakMap Patterns for Leak-Free Code
Imagine a web app that autosaves user data, caches objects for speed, and attaches metadata to DOM nodes. You need fast lookups without turning your cache into a memory black hole.
Why WeakMap
Matters
A regular Map
holds strong references to its keys and values. Even if the key object is no
longer used elsewhere, it stays alive inside the map. Over time, these orphaned entries accumulate and leak
memory.
A WeakMap
, on the other hand, holds weak references to its keys. If there are no other
references to a key object, the GC can reclaim both the object and its associated entry in the
WeakMap
automatically.
Core Characteristics
- Keys must be objects (no primitives).
- Values can be anything.
- Non-enumerable: you can’t loop over entries, preventing accidental retention.
- No
size
,keys()
, orforEach()
. This is by design, so GC can collect safely.
Real-World Use Case: Metadata for DOM Nodes
Suppose you’re building a rich text editor. You need to store extra info for certain nodes, like whether a user has edited a paragraph, but you don’t want to attach properties directly to DOM elements or worry about cleanup when a node is removed.
const nodeMeta = new WeakMap();
function annotate(node, info) {
nodeMeta.set(node, info);
}
function getMeta(node) {
return nodeMeta.get(node);
}
// Usage
const para = document.createElement('p');
annotate(para, { edited: true });
// Later, if `para` is removed and no references remain,
// GC reclaims both the DOM node and its metadata entry.
No manual delete
calls. No risk of stale entries.
Pattern: Caching API Responses
You fetch user profiles and cache them to avoid repeat network calls. Using a Map
can leak if
you never prune old entries. With WeakMap
, you tie cache entries to user objects. Once the user
object falls out of scope, the cache entry goes too.
const profileCache = new WeakMap();
async function getUserProfile(user) {
if (profileCache.has(user)) {
return profileCache.get(user);
}
const profile = await fetch(`/api/users/${user.id}`).then(r => r.json());
profileCache.set(user, profile);
return profile;
}
When user
is no longer referenced, its cache entry vanishes.
Common Pitfalls and Tips
- No iteration:
WeakMap
doesn’t expose its entries. Use it when you don’t need to list all keys. - Avoid primitive keys: only objects work.
- Beware of hidden leaks: storing objects in closures or arrays still counts as strong references.
When Not to Use WeakMap
There are cases where WeakMap
isn’t the right tool:
- Iteration or inspection needed
If you must list all keys or values, say, to generate a report of cached entries, you need aMap
or other structure that exposes its contents.// ❌ WeakMap has no .keys() or .size // ✅ Use Map if you need to log all sessions: const sessionMap = new Map(); // ... console.log(`Active sessions: ${sessionMap.size}`);
- Primitive keys required
WeakMap only accepts objects as keys. If your domain uses strings or numbers as identifiers,WeakMap
won’t work.// ❌ Invalid: WeakMap can’t use string keys const m = new WeakMap(); m.set('user123', { name: 'Bob' }); // TypeError
- Deterministic cleanup or size tracking
You can’t control exactly when the GC runs or when entries disappear. If you need to know when items are removed or track the number of entries, a standardMap
with manual cleanup is safer.// ❌ WeakMap doesn’t fire events on deletion // ✅ Use Map if you need to trigger actions on removal: const cache = new Map(); // manual delete and callback
- Persistent or serializable cache
WeakMap entries vanish without notice and aren’t serializable. For data you must persist or transfer (e.g. saving to IndexedDB), use a plain object, Map, or other storage.
In these scenarios, choose collections that give you full control over their contents and lifecycle.
Conclusion
WeakMap
is a powerful tool for managing memory in JavaScript. It lets you associate data with
objects without preventing garbage collection, making it ideal for metadata, caches, and temporary storage.
By understanding its strengths and limitations, you can write cleaner, more efficient code that avoids
memory leaks.
What’s Next
But WeakMap isn’t the only weak structure in town. In the next post, we’ll explore WeakSet, its similarities, its quirks, and the subtle ways it lets you track objects without ever holding them back.