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 toobj.weakRef.deref()returns the object, orundefinedif 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.
FinalizationRegistrygives 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
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 undefinedThe 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:
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
new WeakRef(obj)creates a weak reference.weakRef.deref()tries to return the original object, if it is still alive.- If the object has already been collected by the GC,
deref()returnsundefined.
The real behaviour looks like this:
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:
| Scenario | Example |
|---|---|
| Leak-free caching | You want to cache objects without holding them in memory forever |
| Memoisation | Keep a computed result for an object while that object is alive |
| Reactive systems and observers | Watch an object without preventing its collection |
| Game engines and graphics | Cache heavy resources (textures, data) but let the GC clear them |
| Integration with FinalizationRegistry | React when an object has been collected |
A plain Map holds its keys strongly:
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:
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:
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:
Object user#1 was removed from memoryImportant: 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:
| Property | Description |
|---|---|
WeakRef does not prevent collection | The object may vanish at any moment |
deref() may return undefined | If the GC has already removed the object |
| You cannot rely on lifetime | The GC is unpredictable |
| Do not use it without a reason | It can lead to "zombie" bugs where an object disappears suddenly |
| Useful only in special scenarios | Caches, memoisation, integration with the GC |
Summary:
| Question | Answer |
|---|---|
| What it does | Creates a weak reference to an object |
| How it works | Does not stop the GC from removing the object |
| Method | .deref() returns the object or undefined |
| Useful for | Caches, memoisation, memory tracking |
| Risks | The object may disappear at any moment |
| Introduced in | ES2021 |
Common mistakes
- Expecting
deref()to always return the object. It can returnundefined; always check the result. - Caching the result of
deref()in a long-lived variable. That creates a strong reference and defeats the point ofWeakRef. - Confusing
WeakRefwithWeakMap.WeakMapmakes its keys weak and cannot be iterated, whileWeakRefis a single reference to a single value. - Treating
FinalizationRegistryas a destructor. The callback may never fire, so you cannot release files or sockets through it. - Using
WeakRefto "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
undefinedafter asetTimeoutwill be flaky.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.