By the end of this lesson, you will be able to use a JavaScript WeakMap to attach data to objects without preventing those objects from being garbage-collected.
What it is
A WeakMap is a key/value collection whose keys must be objects (or symbols in modern JavaScript). The map holds keys weakly, meaning a key stored only in the WeakMap does not keep that object alive. When the object becomes unreachable from the rest of the program, the engine may remove the entry automatically.
Think of it as a side table for objects: you can store metadata, caches, or private state next to an object without adding a property to the object itself. Related terms include Map, WeakSet, garbage collection, and private state.
Why it matters
- It lets you keep private data out of an object’s public shape.
- It can cache results for objects without creating memory leaks.
- It is useful for DOM nodes, event handlers, or temporary metadata.
- It avoids manual cleanup when the key object’s lifetime is controlled elsewhere.
Syntax or steps
Create a WeakMap, then use set, get, has, and delete. Unlike Map, it has no size and cannot be iterated.
const wm = new WeakMap();
wm.set(keyObject, value);
const value = wm.get(keyObject);
const exists = wm.has(keyObject);
wm.delete(keyObject);
Example
const wm = new WeakMap();
function createCounter() {
const counter = {
increment() {
const current = wm.get(this) || 0;
wm.set(this, current + 1);
return current + 1;
}
};
wm.set(counter, 0);
return counter;
}
let a = createCounter();
let b = createCounter();
console.log(a.increment()); // 1
console.log(a.increment()); // 2
console.log(b.increment()); // 1
a = null;
b = null;
// If no other references exist, the counter entries can be collected.
Part by part: wm stores each counter object as a key and its count as the value. increment reads the current count, adds one, and writes it back. Because the count is not a property of counter, outside code cannot inspect or change it directly. When a and b are set to null, the counter objects may become unreachable, allowing their WeakMap entries to be cleaned up.
Common mistakes
- Using primitive keys.
wm.set("id", 1)throws aTypeError. Use an object key, or useMapif primitives are required. - Expecting iteration.
wm.sizeandwm.forEachdo not exist. UseMapwhen you need to enumerate entries. - Assuming immediate deletion. Garbage collection timing is not predictable. Do not rely on cleanup for correctness.
- Keeping strong references elsewhere. If another variable still points to the key object, the entry remains. Remove all references if you want the entry to become collectable.
When to use it
| Feature | WeakMap | Map |
|---|---|---|
| Keys | Objects or symbols | Any value |
| Size and iteration | No | Yes |
| Automatic cleanup | Yes, when keys are unreachable | No |
| Best for | Private metadata, caches, DOM nodes | Ordered collections, enumerable data |
Use WeakMap when the key object’s lifetime controls the data. Use Map when you need to inspect, count, or iterate over entries.
Practice
Guided exercise: Create a WeakMap that stores a click count for button objects. Write trackClick(button) so two calls on the same button return 1 and 2.
Challenge: Use a WeakMap to cache the result of an expensive function that takes an object as its argument.
Hint: Check the cache with wm.get(obj); if missing, compute the value and store it with wm.set(obj, value).
Quick check
Why does WeakMap not provide a size property?
Because entries can disappear during garbage collection, so a stable count would be misleading and could expose objects that are no longer reachable.
Summary
A WeakMap associates values with objects while allowing those objects to be collected when they are no longer reachable. It is best for private metadata and caches where enumeration is unnecessary and automatic cleanup is valuable.