4 ms·
No, they shouldn't. Iterability was not avoided to enable the inverted representation (where an object that is a key in a WeakMap stores a hidden WeakMap entry
by sfink 2y ago
No, they shouldn't. Iterability was not avoided to enable the inverted representation (where an object that is a key in a WeakMap stores a hidden WeakMap entry inside it). And I suspect even with an inverted representation, there's a decent chance there would still be an internal collection of keys for a given WeakMap in order to optimize tracing.
WeakMaps aren't iterable because that would make them a different data structure with different purposes and footguns and forward compatibility. The existing WeakMap is very nice in that there is no observable behavior change if GC starts triggering at a different time. You can't write an application that breaks in a new browser version because the internal GC scheduling was updated, or if your allocation patterns change from when you tested, or whatever. If you have the weakmap and the key, you can get the value. If you don't have one or the other, you can't, and the fact that the GC removes the entry behind your back makes no difference to anything but memory usage.
If you can iterate, then suddenly you can start racing the GC. Let's say your values are shared with some other data structure that uses them, and you have a lastUpdated timestamp on them that you update by iterating over the WeakMap. If you lose a key, you may or may not update lastUpdated on its associated value, depending on when the GC runs. (In this scenario, the value's lifetime is not dependent on the WeakMap; it's always reachable through some other path so you can observe lastUpdated.)
These sorts of footguns now exist via WeakRef and FinalizationGroup, but the entire purpose of those two is to be GC-sensitive. The existing, safe, forwards-compatible WeakMap has many valid uses. Adding iterability would defeat some but not all of those uses. Which means that rather than changing WeakMap, you'd want both WeakMap and IterableWeakMap.
(It would be nice if WeakMap had a different name. It's not "weak" in the sense that WeakRef is weak. A WeakRef says "I won't keep my target alive, but if it's alive for some other reason or GC just hasn't run yet, I'll give it back to you when you ask." A WeakMap says "if a weakmap and one of its keys is alive, I'll keep the value associated with that key alive." It says nothing about whether GC has run. It doesn't care whether something in the rest of the object graph is keeping the key alive.)
Whether engines should support a built-in IterableWeakMap is arguable. It's hard to implement correctly in JS using WeakMap + WeakRef, which argues for its inclusion. But it's also a lot of internal complexity in the engine for something so footgun-shaped. Yet it's really useful when it's what you need. But if it existed, people would be tempted to use it, and probably many or most of those uses would be incorrect (why use the nerfed WeakMap when IterableWeakMap exists? Just don't iterate it, if it bothers you!) But it's not the JS engine's job to prevent coders from writing bad code. But tests could fail intermittently without being buggy. Et cetera.