Use an object for records (a fixed, known set of named fields) and a Map for dictionaries (a growing set of keys that you look up, add and remove).
What a Map gives you that an object does not:
- Any key type. An object key is a string or symbol, so
obj[user]stores under"[object Object]". AMapkeeps the object itself as the key. - SameValueZero equality.
NaNis a usable key, and-0is normalised to+0on insertion. Objects are compared by identity, so two equal-looking literals are different keys. - No inherited keys.
{}already "has"toStringandconstructor, and a user-supplied key like__proto__is a hazard. AMaponly contains what youset. - Insertion order only. An object lists integer-like keys first in ascending order, then strings in creation order. A
Mapiterates strictly in insertion order. - A direct
size, and it is directly iterable (for (const [k, v] of map)), with noObject.keysstep. - Add/remove performance. The spec only requires sublinear average access for both; MDN states that
Mapis optimised for frequent additions and removals. Measure before treating that as a reason on its own.
What an object gives you: literal syntax, dot access, destructuring, native JSON.stringify/parse, and spread. JSON.stringify(map) gives {}, so convert with [...map] or Object.fromEntries.
Setting map.foo = 1 adds an ordinary property to the Map object, not an entry. Always use set, get, has and delete.