A Map or Set holds its keys strongly: while the collection is reachable, every key stays reachable. WeakMap and WeakSet do not count as a reason to keep a key alive. If nothing else references the key object, the engine is allowed to drop the entry. The spec's goal is to let you associate state with an object "in a manner that does not leak memory".
What can be a key. CanBeHeldWeakly accepts objects and symbols that are not in the global registry (Symbol.for). Strings, numbers and other primitives throw a TypeError on set/add; get, has and delete simply return undefined/false for them.
Why you cannot iterate. The spec lets an engine remove entries some time after a key becomes unreachable. If you could list keys, or read size, the answer would depend on when garbage collection ran, which is non-deterministic. So the API only allows operations where you already hold the key: get, set, has, delete on WeakMap, and add, has, delete on WeakSet. There is no size, keys, forEach, iterator or clear.
What you can't rely on. The spec promises nothing about when, or whether, an unreachable entry disappears. Never write code that observes or depends on collection. For observing collection at all, see WeakRef and FinalizationRegistry in the memory-and-gc chapter.
Typical uses:
- Per-object metadata for objects you don't own, such as DOM nodes or third-party instances, without adding properties to them.
- Memoisation keyed by an object, where the cache must not outlive its inputs.
- "Seen" marks with
WeakSet, such as visited nodes in a graph walk. - Private data before
#privatefields; fields are usually simpler now.
Values are held strongly only as long as their key is alive, so a value that references its own key does not keep it alive.