WeakMap
new WeakMap([entries]) WeakMap provides a way to associate data with objects without preventing those objects from being garbage collected. When an object used as a key is no longer referenced anywhere else, it and its corresponding value can be reclaimed by the garbage collector.
Syntax
new WeakMap()
new WeakMap(iterable)
Calling new WeakMap() with no arguments creates an empty WeakMap. When you pass an iterable, each element must be a two-element array — the first is the key (an object), the second is the associated value. Keys that are not objects will throw a TypeError.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
iterable | Array | undefined | An array or other iterable object whose elements are key-value pairs (2-element arrays). |
Examples
Basic usage
const wm = new WeakMap();
const obj = { name: 'example' };
wm.set(obj, 'some value');
console.log(wm.get(obj));
// some value
console.log(wm.has(obj));
// true
wm.delete(obj);
console.log(wm.get(obj));
// undefined
The basic example above shows the four fundamental methods — set, get, has, delete — on a single object key. Once you understand those, the real power of WeakMap becomes apparent when you use it to store private data tied to an object’s lifetime. The next example associates an email and creation date with each User instance without exposing those fields as public properties.
Associating private data
const privateData = new WeakMap();
class User {
constructor(name, email) {
this.name = name;
privateData.set(this, { email, createdAt: new Date() });
}
getEmail() {
return privateData.get(this)?.email;
}
getCreatedAt() {
return privateData.get(this)?.createdAt;
}
}
const user = new User('Alice', 'alice@example.com');
console.log(user.getEmail());
// alice@example.com
console.log(user.name);
// Alice
The User class stores private data behind getter methods, keeping the email and timestamp hidden from external code. That same pattern extends naturally to DOM elements, where you often want to attach metadata — click counts, event handlers, cached measurements — without cluttering the element object itself. Because the WeakMap key is the DOM node, the metadata disappears automatically when the node is removed from the document.
Using with DOM elements
const elementData = new WeakMap();
function trackClick(element) {
const count = elementData.get(element) || 0;
elementData.set(element, count + 1);
console.log(`${element.tagName} clicked ${count + 1} times`);
}
// Elements can be garbage collected when removed from DOM
// and their click counts will be cleaned up automatically
The DOM example attaches click-tracking data that lives and dies with the element. WeakMap also works well for caching: if you compute an expensive result from an object and store it in a WeakMap, the cache entry is freed when the source object is no longer needed elsewhere. The next example caches JSON.stringify() results — the computation only runs once per object, and the cache never grows unbounded.
Cache with automatic cleanup
const computationCache = new WeakMap();
function expensiveComputation(obj) {
if (computationCache.has(obj)) {
return computationCache.get(obj);
}
// Simulate expensive operation
const result = JSON.stringify(obj);
computationCache.set(obj, result);
return result;
}
const data = { heavy: 'data' };
console.log(expensiveComputation(data));
// '{"heavy":"data"}'
// When data is no longer referenced, cache entry can be cleaned up
The computation cache avoids re-running JSON.stringify() for objects it has already seen. A related pattern is attaching lightweight metadata — such as a creation timestamp — to arbitrary objects. The next example uses a WeakMap to record when each object was created and compute its age, without modifying the objects themselves. This is cleaner than monkey-patching properties onto every object.
Object metadata storage
const timestamps = new WeakMap();
function markCreated(obj) {
timestamps.set(obj, Date.now());
}
function getAge(obj) {
return Date.now() - (timestamps.get(obj) || 0);
}
const item = {};
markCreated(item);
setTimeout(() => {
console.log('Age:', getAge(item), 'ms');
}, 100);
Common Patterns
WeakMap is ideal for:
- Private data storage — Associate private state with objects without preventing their cleanup
- Caching — Store cached computations that should be discarded when the cached object is no longer needed
- DOM metadata — Attach data to DOM elements without creating memory leaks
Limitations
- Keys must be objects (not primitives)
- Keys are not enumerable — you cannot iterate over a WeakMap
- Cannot check size with
.sizeproperty - Only
set(),get(),has(), anddelete()methods available
Why WeakMap prevents memory leaks
A regular Map holds strong references to its keys. If you associate data with a DOM element using a Map, the element can never be garbage-collected as long as the Map exists, even after it has been removed from the DOM. A WeakMap holds weak references to its keys: if the only remaining reference to an object is as a WeakMap key, the garbage collector can collect both the key and its associated value. This prevents long-lived Maps from becoming unintentional memory leaks.
WeakMap vs Map
The practical difference: Map is enumerable and has a size property; WeakMap has neither. If you need to iterate the entries or know the count, use Map. If the keys are objects whose lifetime you do not control (DOM nodes, class instances) and you want data to be cleaned up automatically when those objects are collected, use WeakMap.
WeakMap keys must be objects
Only objects (and non-registered symbols in ES2023+) can be used as WeakMap keys. Primitive values — strings, numbers, booleans, null, undefined — are rejected with a TypeError. This restriction exists because weak references require the key to be garbage-collectible; primitives are value types, not heap-allocated objects, so there is nothing to track for collection.