What can be a key. The spec defines a property key as a String or a Symbol. Every other value used in obj[expr] or as a computed key goes through ToPropertyKey: ToPrimitive with string preference, then ToString unless the result is a Symbol. So:
- Numbers become strings:
obj[1],obj['1']andobj[1.0]are one property;-0becomes'0'. - Objects become whatever their
toStringreturns, usually'[object Object]', so two different object keys collide. - A Symbol is kept as is, and is unique.
Symbol('a') !== Symbol('a').Symbol.for('a')is shared through the global registry. - Symbol keys are skipped by
for…in,Object.keys,Object.entriesandJSON.stringify;Object.getOwnPropertySymbolsandReflect.ownKeyssee them.
Order. OrdinaryOwnPropertyKeys lists an ordinary object's own keys in three groups:
- Array indices (canonical numeric strings from
'0'to'4294967294') in ascending numeric order. - Other strings in the order the properties were created.
- Symbols in the order they were created.
Reflect.ownKeys, Object.keys, Object.values, Object.entries, Object.assign, spread and JSON.stringify all follow it, because they all start from [[OwnPropertyKeys]]. '01', '-1' and '1.5' are not array indices, so they sort by creation. Deleting and re-adding a key moves it to the end of its group.
When you need arbitrary keys of any type, or insertion order that ignores integer-looking keys, use a Map.