JavaScript WeakRef Table

PieceWhat it doesField note
new WeakRef(obj)A non-keeping referenceDeref() gives the object IF still alive, else undefined
WeakMap / WeakSet keysCollectable associationsKey collected = entry vanishes - metadata without leaks
FinalizationRegistryCallback after GCcleanup callback runs SOME TIME after collection - no promises
Never force-order GCThe spec stanceNo API guarantees WHEN collection happens - tests cannot rely on it
Deref() null checkThe alive testOnce undefined, always undefined - do not cache the deref
Use case: cachesMemory-bound mapsExpensive objects cached weakly - GC prunes under pressure
cleanup tokensPairing registrationsThe token argument ties a registration to its cancellation
Do not manage resourcesThe honest limitFiles/sockets need explicit close - GC timing is NOT cleanup
Reference: the MDN WeakRef reference. WeakRef and WeakMap keys are references that do NOT keep their target alive: when nothing else holds the object, the collector may reclaim it and deref() starts returning undefined - association without ownership, the metadata-without-leak primitive. The spec's hard stance: nothing guarantees WHEN collection runs, so no code may depend on timing (tests included). Bottom line: weak collections are for caches and object-attached metadata; FinalizationRegistry callbacks are a last-resort telemetry hook; and real resources - files, sockets, workers - need explicit close(), because GC timing is not cleanup scheduling. Related tools: map-set table (the strong collections these weaken), closure table (the leak direction weakrefs cannot fix), and structuredClone table (what survives boundaries vs collection).

WeakRef and WeakMap keys are references that do NOT keep their target alive: when nothing else holds the object, the collector may reclaim it, and deref() starts returning undefined. Association without ownership - the primitive that lets you attach metadata to objects without leaking them.

Bottom line: the spec's hard stance is that nothing guarantees WHEN collection runs - no API forces it, no code may depend on its timing, tests included. Weak collections are for caches and object-attached metadata; FinalizationRegistry is a last-resort telemetry hook; and real resources need explicit close() because GC timing is not cleanup scheduling.

The honest part: the intended uses are narrow by design. The spec's own examples warn against using weakrefs to implement resource management or predict finalization - the moment a program's correctness depends on a deref succeeding, the weakref has become a strongref with worse ergonomics.

How to use

  1. Attach cache metadata weakly: const meta = new WeakMap(); meta.set(domNode, computedData) - when the node leaves the DOM and loses all refs, the entry vanishes with it.
  2. Check aliveness fresh every time: const obj = ref.deref(); if (obj) { use(obj) } - never cache the deref result; a true answer can go false at any pause.
  3. Register teardown telemetry: new FinalizationRegistry(token => stats.collected(token)) - observe, never rely; the callback timing is a suggestion.

Frequently asked questions

What does 'weak' mean in WeakRef and WeakMap keys?

Not participating in liveness. A normal (strong) reference pins its target against garbage collection - as long as you can reach it, it lives. A weak reference observes its target without pinning: if the ONLY references to an object are weak, the collector may reclaim it, and the weakref's deref() returns undefined thereafter (permanently - once dead, always dead). WeakMap inverts the usual direction: its KEY is held weakly, so an entry whose key dies disappears from the map automatically. The use both serve is association-without-ownership: computed values attached to DOM nodes, per-object caches, registry-style metadata that should live exactly as long as the object does.

Why can nothing guarantee when garbage collection runs?

Deliberate spec philosophy. If collection timing were observable and guaranteed, programs would depend on it - and every engine's collector (generational, incremental, concurrent) would have to match one schedule, killing optimizations that make JS fast. So the spec guarantees IF: after an object is truly unreachable, deref() will eventually return undefined and finalization callbacks will eventually run. It refuses to guarantee WHEN - and the observable consequences are real: two identical allocation patterns can collect at different moments on different engines, and a finalization callback may run long after, or never during, the test that expects it. Code that needs deterministic release needs explicit close(), not the collector.

When is a WeakMap the right choice over a regular Map?

When the entry's lifetime should equal the KEY's lifetime, not the map's. The canonical case is associating data with objects you do not own: metadata per DOM node, computed layouts per element, per-request objects - a strong Map would keep every node alive forever after removal (the classic leak), while a WeakMap entry evaporates with its key. The reverse test: if entries must SURVIVE their key's other references dying (a persistent cache by id, a lookup table of records), the Map is correct and a WeakMap would silently lose data. The tell is ownership: WeakMap for annotation, Map for inventory.

Why is FinalizationRegistry not a cleanup mechanism?

Because its callback is a MAYBE, not a THEN. The registry schedules a callback for after an object is collected - but collection timing is unguaranteed, callbacks run on a microtask at an unspecified moment, and registration itself must be paired with unregister() via cleanup tokens or the registry pins the objects it watches (the observer leak). The honest uses are diagnostics and optimization: noticing that cached entries died, freeing auxiliary native-side structures. The dishonest use that breaks production: releasing file handles, database connections or worker resources in the callback - GC may run minutes later or not at all before resource exhaustion, which is why every resource API in the platform is explicit close(), and why the spec's own examples repeat the warning.

Related tools