Step 1 of 5 · Reading · ~4 min
Learn
Proxy and Memory
WeakRef, WeakMap, WeakSet
A garbage collector frees an object only when nothing can reach it. A normal Map or Set reaches everything stored in it, so a cache keyed by objects keeps those objects alive for as long as the cache lives. Weak collections hold their keys without keeping them alive. By the end you can attach data to objects you do not own, and know what you may and may not rely on.
The leak a Map causes
Every el ever passed to track stays in memory, even after the rest of the program has dropped it. The fix is not to remember to delete. It is to use a collection whose reference does not count.
WeakMap: data keyed by object identity
Only objects (and functions) can be keys, because only they have an identity the collector can track. A WeakMap has no size, no keys(), no iteration: an entry may disappear at any moment after its key becomes unreachable, so any count or listing would expose when the collector ran. If the key is already unreachable, no code could look the entry up anyway; if it is reachable, you hold it and can call get.
Two properties follow. The map does not keep the key alive, and a value that refers back to its own key does not keep it alive either. And the cache is by identity, not content: mutating the key does not invalidate the entry.
Private data without a leak
A module-level WeakMap can hold per-instance state that never appears on the instance:
When acct is dropped, its state goes with it. (Private #fields do the same job in new code; the WeakMap pattern is what you meet in older libraries, and it still works for data about objects you did not create.)
WeakSet: a mark that does not pin the object
A WeakSet stores only membership. It is the right tool for "have I already visited this node?" in a graph with cycles, because the marks vanish with the nodes:
WeakRef and FinalizationRegistry
new WeakRef(obj).deref() returns the object if it is still alive and undefined once it has been collected. new FinalizationRegistry(cb) calls cb(heldValue) some time after an object registered with it is collected. Both are for caches and diagnostics, not for logic: collection timing is not specified, may never happen before the program exits, and differs between engines. Always keep the result of deref() in a variable and test it, never call it twice and assume the same answer. They arrived in Node 14.6, so the grader's Node 12 has neither; test with typeof WeakRef === 'function' before relying on them.
Common mistakes
- Using a
Mapfor per-object metadata and calling it a cache. It is a leak with extra steps. - Trying to read
.sizeor loop over aWeakMap. Neither exists; if you need to enumerate, use aMap. - Passing a primitive or
nullas a key. A truthiness check ontypeofis not enough, sincetypeof nullis'object'. - Treating a falsy cached result as a miss:
if (cache.get(k))re-computes every time the answer is0or''. Usehas.
Your exercise
You will write a memoizer that caches per argument object. The starter's area counts how often it really runs, so a cache that misses, caches the wrong thing, or crashes on null shows up in the numbers.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…