Research into weak references (ES2021) beyond the first kneejerk response (“OMG why”) indicated that the response was wholly correct.
Regardless of what the TC9 working group in their “meetings” has “decided” (without input from us, mind you), I currently don’t have an intent to ship this at all.
- The W3C TAG Design Principles recommend against APIs that expose access to the Garbage Collector (which is supposed to be an internal mechanism)
- Garbage collectors are complicated; letting content scripts control (parts of) it is asking for trouble, will likely be detrimental to GC, and is no replacement for proper data handling design in programs.
- This API is wholly unnecessary and extremely implementation-dependent. It makes zero guarantees and the interaction between DOM/html and weakref is very tightly bound to individual engines and cannot be relied on.
- We already have
WeakMapfor large data structures if it’s absolutely necessary. This is not a replacement for it and has a whole bunch of “gotchas” to go along with it.
Background taken from the very TC39 proposal readme:
A note of caution
This proposal contains two advanced features, WeakRef and FinalizationRegistry. Their correct use takes careful thought, and they are best avoided if possible.
Garbage collectors are complicated. If an application or library depends on GC cleaning up a WeakRef or calling a finalizer 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.
- Complex factors may lead to objects being held alive for unexpected amounts of time, such as use with certain APIs.
For this reason, the W3C TAG Design Principles recommend against creating APIs that expose garbage collection. It’s best if WeakRef objects and FinalizationRegistry objects are used as a way to avoid excess memory usage, or as a backstop against certain bugs, rather than as a normal way to clean up external resources or observe what’s allocated.
Another note of caution
Finalizers are tricky business and it is best to avoid them. They can be invoked at unexpected times, or not at all---for example, they are not invoked when closing a browser tab or on process exit. They don’t help the garbage collector do its job; rather, they are a hindrance. Furthermore, they perturb the garbage collector’s internal accounting. The GC decides to scan the heap when it thinks that it is necessary, after some amount of allocation. Finalizable objects almost always represent an amount of allocation that is invisible to the garbage collector. The effect can be that the actual resource usage of a system with finalizable objects is higher than what the GC thinks it should be.
The proposed specification allows conforming implementations to skip calling finalization callbacks for any reason or no reason. Some reasons why many JS environments and implementations may omit finalization callbacks:
- If the program shuts down (e.g., process exit, closing a tab, navigating away from a page), finalization callbacks typically don’t run on the way out.
- If the FinalizationRegistry becomes “dead” (approximately, unreachable), then finalization callbacks registered against it might not run.
It is extremely surprising from a developer’s point of view that this was actually agreed upon to specify in an ES standard. The internals of any JS implementation should never be exposed so directly. Doing so is risky, potentially security-vulnerable (giving programs access to an API to coerce/accurately detect GC happening is... a can of worms), potentially fingerprinting-capable (behavior in response to using the API in specific situations can provide timings of individual browser implementations, etc.) and any GCing language implementation is literally always better internally at performing GC than what a cross-browser script might think from version to version.