WeakRef & FinalizationRegistry
debt(d7/e5/b3/t9)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints.tools list (nvd, cisa-kev, composer-audit, github-advisory) are PHP/security advisory tools unrelated to this JS concept, so they don't apply here. From training knowledge appropriate to JS: no standard linter or static analysis tool reliably catches misuse of WeakRef (e.g., assuming deref() always returns a value, or relying on FinalizationRegistry for prompt cleanup). These bugs are silent in normal testing and only manifest under specific GC pressure, requiring careful code review or targeted runtime/heap profiling to detect.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix field describes a CVE-subscription process unrelated to this JS concept, so it cannot be grounded there. From the common_mistakes: correcting WeakRef misuse typically requires reconsidering the caching or lifecycle strategy — replacing naive WeakRef caches with proper cleanup strategies or strong references, auditing all deref() call sites for undefined handling, and potentially rethinking resource cleanup patterns tied to FinalizationRegistry. This spans multiple call sites across a component but is unlikely to be purely architectural, landing at e5.
Closest to 'localised tax' (b3). WeakRef and FinalizationRegistry are typically used in specific caching or lifecycle management spots rather than spread across an entire codebase. The applies_to scope (web, cli) is broad, but in practice usage is confined to targeted memory-management code. The structural tax is paid by maintainers of those specific modules, not the entire codebase, making this a localised commitment.
Closest to 'catastrophic trap' (t9). The misconception field states explicitly: 'WeakRef prevents the object from being garbage collected while the reference exists — WeakRef specifically allows GC to collect the object; that is the entire purpose; it is the opposite of a strong reference.' This is a direct inversion of the intuitive mental model — a developer who has never used WeakRef will naturally assume holding a reference keeps the object alive (which is true for every other reference type). Additionally, common_mistakes include assuming deref() always returns a value and using FinalizationRegistry for prompt cleanup, both of which are catastrophically wrong in subtly unpredictable ways tied to GC non-determinism.
Also Known As
TL;DR
Explanation
WeakRef (ES2021): holds a reference that does not prevent garbage collection. weakRef.deref() returns the object or undefined if collected. Use case: caches where objects should be GC'd when no longer used elsewhere — prevents memory leaks from strong cache references. FinalizationRegistry: register a callback that fires when an object is garbage collected — for releasing external resources. Caveat: GC timing is non-deterministic; deref() can return undefined at any point; never write code that depends on GC timing.
Common Misconception
Why It Matters
Common Mistakes
- Depending on WeakRef.deref() always returning a value — it may return undefined at any GC cycle
- Using WeakRef as a general cache without a cleanup strategy for dead Map entries
- FinalizationRegistry for resource cleanup that must happen promptly — GC timing is non-deterministic
- Confusing WeakRef (direct object reference) with WeakMap (objects as keys)
Code Examples
// Strong cache reference — objects never GC'd:
const cache = new Map();
function getExpensiveObj(key) {
if (!cache.has(key)) cache.set(key, createExpensive(key));
return cache.get(key); // Object kept alive forever by Map
}
// WeakRef cache — objects GC'd when no longer used elsewhere:
const cache = new Map();
const registry = new FinalizationRegistry(key => {
cache.delete(key); // Clean up dead Map entries
});
function getExpensiveObj(key) {
const cached = cache.get(key)?.deref();
if (cached !== undefined) return cached; // Still alive
const obj = createExpensive(key);
cache.set(key, new WeakRef(obj)); // Weak — GC can collect
registry.register(obj, key); // Cleanup Map entry on GC
return obj;
}