Why does a number as key worked for a string key in an Object and vice-versa (Javascript)

Viewed 696

Curious about the following two cases:

First:

const key = 2;
const obj = { "2": "stackoverflow" };
obj[key]; //results in "stackoverflow" but the key is 2 and not "2"

Second:

//Vice versa case
const otherKey = "21";
const otherObj = { 21: "youtube" };
otherObj[otherKey]; //results in "youtube" but the key is "21" and not 21

My conclusion:

That since keys should be string and while finding key (when key is seemingly a number) existence in Javascript objects it does so by type-conversion comparison and not by strict or a string conversion.

Is there more to this why these cases work and am more interested in the how bit of that?

3 Answers

What happens here is something called type coercion, where a value of one type is converted to some other type for doing some operation using said value.

Since objects in Javascript store keys and values, and the keys are stored as strings, when you provide a numeric value as the key to the object, it is coerced to a string and then looked up in the object.

It'd be a little clearer if we took the example of an array. Arrays store values at indexes, which are numeric. So if we have var a = [4, 3, 5], looking up the value at index 2 using a a[2] and a["2"] would give you the same result 5, since the string "2" is coerced to a number type to support the lookup operation in the array.

Hope that helps.

The relevant bits of the standard are

12.3.2.1 Runtime Semantics: Evaluation

MemberExpression:MemberExpression[Expression]

...6. Let propertyKey be ? ToPropertyKey(propertyNameValue).

and

7.1.14 ToPropertyKey ( argument )

  1. Let key be ? ToPrimitive(argument, hint String).

  2. If Type(key) is Symbol, then Return key.

  3. Return ! ToString(key).

In plain English, in object[whatever], whatever is converted to a string, unless it's a symbol.

Illustration:

let s = Symbol();

let o = { 
  '12': 1,
  'foo': 2,
  'true': 3,
  [s]: 4
}

console.log(o[6*2])
console.log(o[{toString: () => 'foo'}])
console.log(o[1 === 1])
console.log(o[s])

The behaviour of object initializers is described in 12.2.6.7 and is exactly the same.

From MDN Docs here

Property names must be strings. This means that non-string objects cannot be used as keys in the object. Any non-string object, including a number, is typecasted into a string via the toString method.

So, basically the keys are type coerced to strings even if they are numbers.

const object = { 21 : 'test', '21': 'message' };

console.log(object)

Related