WeakRef
new WeakRef(target) WeakRef is a built-in JavaScript object introduced in ES2021 that lets you hold a weak reference to another object. Unlike a normal reference, a WeakRef doesn’t prevent the garbage collector from reclaiming the target when there are no strong references left. This makes it useful for caches and registries where you’d rather let memory be freed automatically than manage cleanup yourself.
Constructor
new WeakRef(target)
Creates a new WeakRef instance that points to target.
Parameters:
target— Any object or non-registered symbol. It cannot be a registered symbol (one created withSymbol.for()).
Returns: A new WeakRef instance
Example:
const obj = { name: "example" };
const ref = new WeakRef(obj);
console.log(ref instanceof WeakRef); // true
console.log(ref.deref()); // { name: "example" }
The constraint on target types matters: you cannot create a WeakRef to a symbol from the global symbol registry. Non-registered symbols (created with Symbol()) work fine, though that’s rarely useful in practice.
Instance Method
WeakRef.prototype.deref()
Retrieves the referenced object, if it hasn’t been garbage-collected yet.
Parameters: None
Returns: The target object, or undefined if the target has been collected.
Example:
const ref = new WeakRef({ name: "example" });
console.log(ref.deref()); // { name: "example" }
// After garbage collection reclaims the object:
console.log(ref.deref()); // undefined
Calling deref() on a collected WeakRef gives you undefined. The tricky part is that you cannot rely on when or whether garbage collection runs. An object might never be collected, even if nothing else strongly holds it, so deref() may return the target indefinitely.
Garbage Collection Behavior
WeakRef exposes some key guarantees from the ECMAScript spec:
Intra-job visibility. Within the same JavaScript job (a single turn of the event loop), if a strong reference exists anywhere, calling deref() always returns the target. The GC won’t yank it out from under you mid-computation.
Consistency among WeakRefs. If you hold multiple WeakRef instances pointing to the same target, calling deref() on all of them from the same job returns consistent results. Either every call returns the object, or every call returns undefined.
Interaction with FinalizationRegistry. If a target is also registered with a FinalizationRegistry, the WeakRef’s target is cleared at the same time or before the cleanup callback fires. If you call deref() inside a finalizer, you get undefined.
Immutability. A WeakRef always points to its original target. You cannot reassign it.
Caching with Automatic Eviction
The canonical use case is a cache that doesn’t prevent GC. A plain Map holds strong references to both keys and values, so cached objects never get collected. Using a WeakRef as the value solves this:
const cache = new Map();
function getCachedData(key, createFn) {
const cached = cache.get(key);
if (cached) {
const data = cached.deref();
if (data) return data;
}
const data = createFn();
cache.set(key, new WeakRef(data));
return data;
}
If nothing else holds a strong reference to data, the WeakRef in the map doesn’t stop the GC from reclaiming it. The next call recomputes the value. This pattern works well for memoization caches where stale data is acceptable.
Tracking DOM Elements
A WeakRef to a DOM element lets you track whether the element is still in the document:
class Tracker {
constructor(element) {
this.ref = new WeakRef(element);
this.count = 0;
}
tick() {
const el = this.ref.deref();
if (el) {
el.textContent = ++this.count;
} else {
console.log("Element was removed from DOM.");
this.ref = null;
}
}
}
// Usage:
const div = document.createElement("div");
document.body.appendChild(div);
const tracker = new Tracker(div);
tracker.tick(); // div shows "1"
tracker.tick(); // div shows "2"
tracker.tick(); // div shows "3"
document.body.removeChild(div); // remove from DOM
tracker.tick(); // "Element was removed from DOM."
When the element is removed from the DOM and no other strong references remain, deref() returns undefined. Your code can detect this and stop working on a dead element.
Gotchas
Non-deterministic GC timing. You cannot predict when (or if) the garbage collector will run. Code that depends on deref() returning undefined at a specific moment will fail unpredictably across engines and runtime conditions.
Maps hold strong references. Storing an object directly in a Map or Array keeps it alive. Use a WeakRef as the value if you want the map to be transparent to GC. (WeakMap and WeakSet already handle this for keys.)
Closures can hold targets alive. If a closure captures a WeakRef and that closure is referenced strongly elsewhere, the closure itself may prevent the target from being collected. This can silently break the pattern.
Browser compatibility. Available in all modern browsers since 2021 (V8, SpiderMonkey, JavaScriptCore). Not available in IE11. Node.js 14+ supports it behind a flag; Node.js 16+ has full support.
See Also
- /reference/map-and-set/weakmap/ — Key/value pairs where keys are weakly held
- /reference/map-and-set/weakset/ — Sets where members are weakly held