Skip to main content

What WeakRef does

WeakRef (weak reference) is an object that holds a weak reference to another object, that is, a reference which does not stop the garbage collector from removing that object. Put simply, WeakRef lets you remember an object while it is alive without artificially keeping it in memory.

Theory

TL;DR

  • new WeakRef(obj) creates a weak reference to obj.
  • weakRef.deref() returns the object, or undefined if the GC has already collected it.
  • Unlike a normal reference, a weak one does not prevent garbage collection.
  • The moment the GC runs is unpredictable, so the object's lifetime cannot be guaranteed.
  • Useful in narrow scenarios: leak-free caches, memoisation, observers, heavy resources.
  • FinalizationRegistry gives you a callback when an object is collected, with no timing guarantees.
  • Added in ES2021; ordinary code usually has no need for it.

Quick example

javascript
let user = { name: 'Tim' }; const weakRef = new WeakRef(user); user = null; // there are no strong references any more // ...later, maybe... console.log(weakRef.deref()?.name); // 'Tim' or undefined

The object can now be removed by the GC at any moment, and then weakRef.deref() returns undefined.

Strong and weak references

An ordinary (strong) reference keeps the object in memory:

javascript
let user = { name: 'Tim' }; let ref = user; // an ordinary, strong reference user = null; // but ref still holds the object console.log(ref.name); // 'Tim'

The object will not be removed by the garbage collector as long as at least one strong reference (ref) exists.

A weak reference behaves differently: it is not counted when deciding whether the object is still needed.

How WeakRef works

  1. new WeakRef(obj) creates a weak reference.
  2. weakRef.deref() tries to return the original object, if it is still alive.
  3. If the object has already been collected by the GC, deref() returns undefined.

The real behaviour looks like this:

javascript
let ref; (() => { let bigObject = { data: 'Some heavy data...' }; ref = new WeakRef(bigObject); })(); // bigObject is no longer reachable directly setTimeout(() => { console.log(ref.deref()); // either the object or undefined, up to the GC }, 1000);

The garbage collector runs at an unspecified moment. There is no way to predict when exactly deref() will start returning undefined.

Why WeakRef exists

Normally JavaScript does not ask you to manage memory by hand, but there are narrow cases where WeakRef helps:

ScenarioExample
Leak-free cachingYou want to cache objects without holding them in memory forever
MemoisationKeep a computed result for an object while that object is alive
Reactive systems and observersWatch an object without preventing its collection
Game engines and graphicsCache heavy resources (textures, data) but let the GC clear them
Integration with FinalizationRegistryReact when an object has been collected

A plain Map holds its keys strongly:

javascript
const cache = new Map(); function getUserData(user) { if (!cache.has(user)) { cache.set(user, heavyCompute(user)); } return cache.get(user); }

The problem is that even when user is no longer needed, the Map will not let the GC remove it until the cache is cleared by hand. The fix is to store a weak reference in the cache:

javascript
const cache = new Map(); function getUserData(user) { const ref = cache.get(user); let data = ref?.deref(); if (!data) { data = heavyCompute(user); cache.set(user, new WeakRef(data)); } return data; }

Now, if data is not used anywhere else, the GC frees it and ref.deref() simply returns undefined.

Pairing with FinalizationRegistry

To find out when an object was collected by the GC, use FinalizationRegistry:

javascript
const registry = new FinalizationRegistry(id => { console.log(`Object ${id} was removed from memory`); }); let user = { name: 'Tim' }; registry.register(user, 'user#1'); let ref = new WeakRef(user); user = null;

When the GC collects user, the callback fires:

text
Object user#1 was removed from memory

Important: the call is not guaranteed in time and may not happen at all before the process ends.

When not to use WeakRef

Do not use it:

  • to manage ordinary data;
  • to "speed up" the GC, which is pointless;
  • to store state in React, Vue or Next.js;
  • in critical logic chains where the result cannot suddenly disappear.

What to keep in mind:

PropertyDescription
WeakRef does not prevent collectionThe object may vanish at any moment
deref() may return undefinedIf the GC has already removed the object
You cannot rely on lifetimeThe GC is unpredictable
Do not use it without a reasonIt can lead to "zombie" bugs where an object disappears suddenly
Useful only in special scenariosCaches, memoisation, integration with the GC

Summary:

QuestionAnswer
What it doesCreates a weak reference to an object
How it worksDoes not stop the GC from removing the object
Method.deref() returns the object or undefined
Useful forCaches, memoisation, memory tracking
RisksThe object may disappear at any moment
Introduced inES2021

Common mistakes

  • Expecting deref() to always return the object. It can return undefined; always check the result.
  • Caching the result of deref() in a long-lived variable. That creates a strong reference and defeats the point of WeakRef.
  • Confusing WeakRef with WeakMap. WeakMap makes its keys weak and cannot be iterated, while WeakRef is a single reference to a single value.
  • Treating FinalizationRegistry as a destructor. The callback may never fire, so you cannot release files or sockets through it.
  • Using WeakRef to "help" the garbage collector. The GC runs on its own schedule; a weak reference does not speed it up.
  • Writing tests that ignore how unpredictable the GC is. A test expecting undefined after a setTimeout will be flaky.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.