WeakRef & FinalizationRegistry
Weak references that don't prevent garbage collection, and callbacks that fire when objects are collected — when and why to use them.
Only objects (and non-registered symbols) can be held weakly. new WeakRef(10000), or a WeakRef to a string, throws TypeError: WeakRef: invalid target. It's an easy crash to ship in a weak cache: the code is tested with objects, then one day a computed value happens to be a number or a string.
const cache = new Map();
function remember(key, value) {
cache.set(key, new WeakRef(value));
}
remember("report", { rows: 10000 }); // an object: fine
remember("rowCount", 10000); // a number
// TypeError: WeakRef: invalid targetReferences that step aside for the garbage collector
Normally, if you have a variable pointing to an object, that object stays in memory forever — as long as your variable exists. This is called a "strong reference." A weak reference is different: it points to an object but doesn't prevent the garbage collector from deleting it if nothing else holds a strong reference.
WeakRef — check if an object is still alive
// Create a WeakRef to an object:
let server = { host: "localhost", port: 3000 };
const ref = new WeakRef(server);
// .deref() returns the object if it's still alive, or undefined if GC'd:
const alive = ref.deref();
if (alive) {
console.log(`Server: ${alive.host}:${alive.port}`); // "Server: localhost:3000"
} else {
console.log("Server was collected");
}
// Always use ?. with deref() — never assume it's alive:
console.log(ref.deref()?.host); // "localhost" (or undefined if collected)FinalizationRegistry — get notified when an object is collected
// Register a callback to fire when an object is garbage collected:
const registry = new FinalizationRegistry((heldValue) => {
// 'heldValue' is what you passed when registering — NOT the object itself
// (the object is already gone when this fires!)
console.log(`Cleanup for: ${heldValue}`);
});
function createResource(name) {
const resource = { name, data: new Array(10000).fill(0) };
registry.register(resource, name); // second arg = heldValue for the callback
return resource;
}
let r = createResource("database");
// ... use r ...
r = null; // release strong reference
// At some future point, GC runs and the callback fires:
// "Cleanup for: database"The timing problem — why this is tricky
// Garbage collection is non-deterministic.
// You cannot predict WHEN the callback fires.
// You cannot force it to run.
// In some implementations it may never run (e.g. very short programs).
// This means FinalizationRegistry callbacks:
// ✓ Can do logging, metrics, optional cleanup
// ✗ Cannot be relied on for critical cleanup (use try/finally instead)
// ✗ Cannot be used for security-sensitive cleanup
// ✗ Cannot guarantee ordering relative to other codeOfficial Sources
- MDN Web Docs — WeakRef
- MDN Web Docs — FinalizationRegistry
- TC39 WeakRefs Proposal
- ECMAScript 2024 — WeakRef (§26.1)
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.