JavaScript WeakMap and WeakRef
Store object-keyed data and hold references without blocking garbage collection in JavaScript.
TL;DR
- Store object-keyed properties using weak references to support garbage collection.
- Hold object references without preventing the engine garbage collection process.
- Use
FinalizationRegistryobjects to configure cleanup callbacks for collected references.
What WeakMap Is
WeakMap key valuesStores key-value entries where keys are object references.
const cache = new WeakMap();
const user = { id: 1 };
cache.set(user, { clicks: 3 });Weak referencesAllows garbage collection of key objects when references disappear.
let el = document.querySelector("#widget");
cache.set(el, { clicks: 0 });
el = null; // entry can be collected nowKey constraintsRequires object keys, throwing errors when primitives are passed.
// cache.set("key", 1); // TypeErrorNon-Enumerable design
No iterationProhibits access to size, keys, and values properties.
const wm = new WeakMap();
console.log(wm.size); // undefined
// [...wm]; // TypeError: wm is not iterableDeterministic safetyHides garbage collector actions to keep behavior predictable.
// Non-enumerable design prevents observationPrivate Data Caches
Property encapsulationAttaches private attributes to objects without changing structures.
const privateData = new WeakMap();
class Account {
constructor(b) { privateData.set(this, { b }); }
getBal() { return privateData.get(this).b; }
}Metadata attachmentCaches calculated values keyed to specific element instances.
const sizeCache = new WeakMap();
function getBounding(el) {
if (!sizeCache.has(el)) {
sizeCache.set(el, el.getBoundingClientRect());
}
return sizeCache.get(el);
}WeakRef
Object wrappersWraps object references without blocking garbage collection.
let obj = { data: "large" };
const ref = new WeakRef(obj);
ref.deref(); // { data: "large" }deref() checksQueries deref references, verifying presence before accesses.
obj = null;
// after GC executes:
const target = ref.deref();
if (target) console.log(target.data);FinalizationRegistry
Cleanup callbacksRegisters callbacks to execute after objects are collected.
const reg = new FinalizationRegistry(held => {
console.log("collected:", held);
});
reg.register(obj, "label");Tips
- Attach metadata records to external object keys safely using self-cleaning
WeakMapcaches. - Create memory-sensitive caches using
WeakRefto let engines collect references under pressure.
Warnings
- Avoid setting primitive keys on
WeakMapbecause keys must always be object references. - Do not rely on
FinalizationRegistryfor critical updates since callback execution timing is unpredictable.
In Practice
Caches DOM bounding rectangles using a WeakMap to automatically purge cache data when nodes are removed.
- Create a WeakMap instance to hold the DOM element cache.
- Check if the cache contains bounding rects for the query element.
- Calculate bounds using the element's bounding rect API.
- Store calculated bounds in the cache mapped to the element.
- Return cached bounds directly without executing repetitive calculations.
function createBoundingCache() {
const cache = new WeakMap();
function getBounds(element) {
if (!cache.has(element)) {
const rect =
element.getBoundingClientRect();
cache.set(element, rect);
}
return cache.get(element);
}
return { getBounds };
}FAQ
Map objects retain strong references to their keys, preventing garbage collection. WeakMap objects hold weak references, allowing keys to be collected. Keys must be objects, and iteration is prohibited.
Garbage collection timing is non-deterministic. Exposing entry lists would reveal when memory recovery occurred. The specification prohibits size and iteration properties to prevent engine leakage.
A WeakRef holds object references without preventing garbage collection. Call .deref() to retrieve targets. This method returns undefined if the object has already been garbage collected.
Use private fields for instance attributes on classes you write. Reach for WeakMap to store private data on foreign instances or plain objects that do not support private fields.
No, engines may skip callbacks if programs exit early. Treat these execution routines as non-critical optimizations. Always use explicit cleanups or try/finally blocks for essential operations.