Weak references
Baseline Widely available
Supported in Chrome: yes.
Supported in Edge: yes.
Supported in Firefox: yes.
Supported in Safari: yes.
This feature is well established and works across many devices and browser versions. It’s been available across browsers since April 2021
Every time you create an object in JavaScript, it takes up memory in your browser. When you’re done with that object, JavaScript is supposed to clear that memory automatically. But sometimes, this cleanup doesn’t happen when you expect it to.
Strong References
A strong reference is like putting a “do not delete” tag on data in memory. When you create a normal variable in JavaScript that points to an object or value, you’re creating a strong reference.
As long as this reference exists, JavaScript’s garbage collector won’t remove that data from memory, even if it’s not being used anywhere else.
Here, the user
variable strongly holds onto the object, keeping it safe from garbage collection.
Weak References
A weak reference, on the other hand, is like putting a “delete if not needed” tag on data. It points to an object or value but doesn’t prevent the garbage collector from removing that data if no strong references exist. Think of it as a way to reference something without forcing it to stay in memory.
To put it simply: if an object only has weak references pointing to it, the garbage collector can clean it up. This is particularly useful when you want to reference something temporarily without causing memory leaks.
The key difference is that strong references actively prevent garbage collection, while weak references allow it to happen when no strong references remain.
Why Memory Cleanup Matters
Let’s say you’re building a chat app. Every time a new message appears, you format it and save some information about how it should look:
As users send more messages, your app uses more memory. Even when old messages scroll away and you can’t see them anymore, the data stays in memory because messageCache
still has a reference to it. Your browser can’t clean it up because you’re telling it “I might need this later” by keeping it in the cache.
How Memory Cleanup Works
By default, JavaScript keeps objects in memory as long as your code can still reach them. Think of it like a game of connect-the-dots. If JavaScript can draw a line from your code to an object through references, that object stays in memory.
What WeakRef Does
WeakRef
creates a special kind of reference that doesn’t force objects to stay in memory. It’s like taking a note about where something is without actually holding onto it.
When you use WeakRef
, you’re telling JavaScript “I’d like to use this object if it’s still around, but it’s okay to clean it up if nothing else needs it.”
WeakRef
can help when you want to track something without forcing it to stay in memory. Here’s a real example where that’s useful - tracking images as they load on a page:
This code helps us understand how long images take to load, but won’t prevent the browser from cleaning up the image if the user leaves the page or if the image gets removed.
Without WeakRef
, we’d need to manually remove our tracker every time an image was removed, or the image would stay in memory even when it’s gone from the page.
Cleaning Up After Objects with FinalizationRegistry
Sometimes you need to know when an object gets cleaned up from memory. That’s where FinalizationRegistry
helps. It lets you run code when an object is removed from memory.
But be careful - you can’t rely on exactly when the cleanup function will run. The browser decides when to clean up memory, and your cleanup function might run much later than you expect. This makes FinalizationRegistry
useful for:
- Cleaning up files or resources on a server
- Logging when objects are removed
- Updating counters or statistics
Here’s a more practical example combining WeakRef
and FinalizationRegistry
to track memory usage.
This code helps track memory usage over time, updating statistics when objects are created and cleaned up, without preventing the cleanup from happening.
Here are some specific points included by the authors in the proposal that introduced WeakRef
Garbage collectors are complicated. If an application or library depends on GC cleaning up a
WeakRef
or calling a finalizer [cleanup callback] in a timely, predictable manner, it’s likely to be disappointed: the cleanup may happen much later than expected, or not at all. Sources of variability include:
- One object might be garbage-collected much sooner than another object, even if they become unreachable at the same time, e.g., due to generational collection.
- Garbage collection work can be split up over time using incremental and concurrent techniques.
- Various runtime heuristics can be used to balance memory usage, responsiveness.
- The JavaScript engine may hold references to things which look like they are unreachable (e.g., in closures, or inline caches).
- Different JavaScript engines may do these things differently, or the same engine may change its algorithms across versions.
- Complex factors may lead to objects being held alive for unexpected amounts of time, such as use with certain APIs.
If you’re unsure whether WeakRef
is right for your use case, consider avoiding it. It’s a powerful tool, but it’s easy to misuse. If you’re not sure, you probably don’t need it.