How do WeakRef and FinalizationRegistry differ from normal JavaScript references, and when might they be useful in a Node.js application? · Binary Semaphore
·7 min read
How do WeakRef and FinalizationRegistry differ from normal JavaScript references, and when might they be useful in a Node.js application?
Node.js Interview Question #113
Question :
How do WeakRef and FinalizationRegistry differ from normal JavaScript references, and when might they be useful in a Node.js application?
Detailed Solution:
1. Normal JavaScript References
A normal reference is a strong reference.
let user = {
name: "Rahul",
};
let anotherReference = user;
user = null;
The object is still reachable through anotherReference.
Conceptually:
user ───────► Object
▲
│
anotherReference
Because at least one strong reference exists, the garbage collector cannot remove the object.
If both references disappear:
user = null;
anotherReference = null;
then the object may eventually become eligible for garbage collection.
2. What is WeakRef?
WeakRef provides a weak reference to an object.
Unlike a normal reference, a WeakRef does not keep the object alive.
Example:
let user = {
name: "Rahul",
};
const weakUser = new WeakRef(user);
console.log(weakUser.deref());
deref() gives you the object if it is still alive.
You might get:
{ name: "Rahul" }
However, if the object becomes unreachable elsewhere:
user = null
↓
Object becomes potentially unreachable
↓
Garbage collector decides when to run
↓
Object is collected
↓
FinalizationRegistry callback may be scheduled
FinalizationRegistry can potentially serve as a backup mechanism, but it should not be treated as deterministic cleanup.
11. A Subtle Problem with WeakRef
There is another important concept.
Consider:
const weak = new WeakRef(object);
const obj = weak.deref();
if (obj) {
// Use obj
}
Once deref() returns the object, you have a strong reference to it through obj for as long as obj remains reachable.
So:
const obj = weak.deref();
temporarily brings the object back into the normal strong-reference world.
That's why code using WeakRef should be designed carefully.
12. Node.js Use Cases
In real Node.js applications, these features can be useful in situations such as:
1. Memory-sensitive caches
You want cached objects to disappear naturally when memory pressure causes them to become unreachable.
2. Object metadata
You want to associate information with an object without keeping that object alive.
For this, WeakMap is often more appropriate than WeakRef.
3. Debugging
FinalizationRegistry can help observe whether objects are eventually being garbage-collected.
4. Large in-memory objects
For objects that are expensive to recreate but shouldn't be kept alive solely because of a secondary reference, WeakRef can sometimes be useful.
5. Native-resource bookkeeping
In advanced Node.js applications, finalization mechanisms can sometimes be used as a safety net around resources managed outside normal JavaScript memory management.
But deterministic cleanup is still preferred.
13. WeakRef vs FinalizationRegistry
They solve related but different problems.
WeakRef
Answers:
"Can I access this object if it is still alive without keeping it alive?"
Example:
const ref = new WeakRef(object);
const objectIfAlive = ref.deref();
FinalizationRegistry
Answers:
"Can I be notified after this object has become unreachable and been garbage-collected?"