WeakRef

Holds a reference to an object without preventing that object from being garbage collected.

Signature
new WeakRef(target)
  • Added in JavaScript ES2021

Parameters

ParameterTypeDescription
$targetobject | symbolThe object (or non-registered symbol) to hold weakly. The target cannot be changed after construction.

Return value

WeakRefA new WeakRef object whose target is weakly referenced. It can only be constructed with new; calling it as a function throws.

Errors

The constructor throws a TypeError if target is not an object or a non-registered symbol, and if called without new. deref() never throws; it returns undefined once the target has been reclaimed.

Changelog

  • PHP ES2021Introduced together with FinalizationRegistry. Baseline widely available since April 2021.
  • PHP Later revisionNon-registered symbols became valid targets alongside objects.

WeakRef gives you a handle on an object that does not count as a reason to keep it in memory. Call deref() to get the target back, or undefined if the engine has already reclaimed it. The target is fixed at construction and can never be reassigned.

The guarantees you actually get

Within a single job — the current synchronous run plus its promise reactions — the target will not be reclaimed after you have created the WeakRef or called deref(), and all WeakRefs to the same target agree with each other. Collection is only ever observable between turns of the event loop. Everything else is up to the engine: a target may be reclaimed far later than you expect, or never.

What trips people up

This is the tool of last resort. If your key is an object and your value is metadata, WeakMap is almost always the right answer and needs none of this reasoning. Reach for WeakRef only when you need the value side to be droppable — a cache of large objects that may be evicted, or a long-lived observer that should not keep its subject alive.

Never write logic that depends on deref() eventually returning undefined. Correct code must work identically whether or not the target is still there.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Keep a memory-sensitive cache of large parsed objects that may be dropped under pressure
  • Let a long-lived observer or timer stop itself once its subject is gone
  • Hold a back-reference from a child to a parent without creating a retain cycle
  • Store the last result of an expensive computation without pinning it in memory

Frequently asked questions

When does deref() return undefined?

Only after the target has become unreachable from anywhere else and the engine has actually collected it, which can happen at any time between event loop turns — or never. Within one job, once you have called deref() the target is guaranteed to stay alive.

Should I use WeakRef or WeakMap?

Use WeakMap when you want to attach data to an object key — it is simpler and needs no liveness checks. Use WeakRef only when the stored value itself must remain collectable, such as a cache of large objects.

Can I force the target to be collected for testing?

No — garbage collection is not exposed to JavaScript and its timing is deliberately unobservable, so no standard call will make deref() return undefined on demand. Write code that behaves correctly in both cases and test the undefined branch by injecting a stub with a deref() that returns undefined.

More examples

A cache that can be evicted

The Map holds WeakRefs, not the reports themselves, so a cached report can be reclaimed once nothing else uses it. get() must handle a live entry whose deref() has gone undefined and clean up the stale key — that branch is not optional.

Example · javascript
const cache = new Map();

function put(key, value) {
  cache.set(key, new WeakRef(value));
}
function get(key) {
  const value = cache.get(key)?.deref();
  if (!value) cache.delete(key); // drop the empty husk
  return value;
}

const report = { rows: 10_000 };
put("q1", report);
console.log(get("q1")?.rows); // 10000
console.log(get("missing")); // undefined

An observer that does not keep its subject alive

The poller can outlive its subject without leaking it: every tick re-derefs and quietly stops when the subject is gone. Holding subject directly would keep the whole object alive for as long as the poller exists.

Example · javascript
class Poller {
  constructor(subject) {
    this.ref = new WeakRef(subject);
  }
  tick() {
    const subject = this.ref.deref();
    if (!subject) return "stopped";
    subject.count++;
    return `count=${subject.count}`;
  }
}

const subject = { count: 0 };
const poller = new Poller(subject);
console.log(poller.tick()); // count=1
console.log(poller.tick()); // count=2

Prefer WeakMap when you can

The WeakMap version needs no undefined check because the entry simply disappears with its key. Use WeakRef only when the thing you want collectable is the value you are storing, not the key you look it up by.

Example · javascript
const owner = { id: 1 };

// Metadata keyed by an object: WeakMap, no deref dance.
const meta = new WeakMap([[owner, { hits: 0 }]]);
meta.get(owner).hits++;
console.log(meta.get(owner)); // { hits: 1 }

// WeakRef is for making the *value* droppable:
const held = new WeakRef(owner);
console.log(held.deref() === owner); // true

Discussion

  • Be the first to comment on this lesson.