Keys in JavaScript Objects Can Only Be Strings

Keys in Javascript objects can only be strings?

JavaScript's built-in objects do provide hashtable functionality using
the square brackets notation for properties, provided your keys are
strings or numbers

That seems to be incorrect - object keys are always strings may be strings or (since ECMAScript 2015, aka ECMA-262 ed 6) symbols. But that is a different topic to square bracket property access.

See ECMA-262 ed 3 § 11.2.1 (Please also see ECMAScript 2017 (draft).):

Properties are accessed by name, using either the dot notation:

MemberExpression . IdentifierName

CallExpression . IdentifierName

or the bracket notation:

MemberExpression [ Expression ]

CallExpression [ Expression ]

The dot notation is explained by the following syntactic conversion:

MemberExpression . IdentifierName

is identical in its behaviour to

MemberExpression [ <identifier-name-string> ]

and similarly

CallExpression . IdentifierName

is identical in its behaviour to

CallExpression [ <identifier-name-string> ]

where <identifier-name-string> is a string literal containing the
same sequence of characters after processing of Unicode escape
sequences as the IdentifierName.

So when using dot notation, the bit after the dot must fit the criteria for an IdentifierName. But when using square brackets, an expression is provided that is evaluated and resolved to a string.

Briefly, square bracket notation is provided so that properties can be accessed using an expression, e.g.

var y = {};
var x = 'foo';
y[x] = 'foo value';

In the above, x is provided in square brackets so it is evaluated, returning the string 'foo'. Since this property doesn't exist on y yet, it is added. The foo property of y is then assigned a value of 'foo value'.

In general terms, the expression in the square brackets is evaluated and its toString() method called. It is that value that is used as the property name.

In the dot property access method, the identifier is not evaluated, so:

y.bar = 'bar value';

creates a property bar with a value bar value.

If you want to create a numeric property, then:

y[5] = 5;

will evaluate 5, see it's not a string, call (more or less) Number(5).toString() which returns the string 5, which is used for the property name. It is then assigned the value 5, which is a number.

Edit

This answer was written when ECMAScript ed3 was current, however things have moved on. Please see later references and MDN.

Set() as key for Javascript object

Object keys can only be strings (or Symbols, rarely). If you try to assign a key which is not a string, it will be cast to a string before being put onto the object.

See what happens when you try to turn a Set into a string:

let set1 = new Set([1]);let set2 = new Set([2]);console.log(  String(set1),  String(set2));

Can't force object keys to be integer

Object keys are always strings. You can see more about it 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.

For you to be able to achieve what you want you will need to cast the keys back to integers.

JavaScript Symbol type: (non-string object keys)

I thought object keys were strings only

You're right, but that was true for EcmaScript 5 only. ES 6 / harmony is a draft for something new!

I'm trying to make sense of the spec

It's a draft only, rapidly changing. How symbols are used and whether or how they can be created by arbitrary scripts does not seem to have settled yet (scan through the versions for changes).

If you scroll down to the very end of that document (even below Annex F), you for example will see a Section 8.4.4: Symbol Exotic Objects that has been moved out there. It states

Exotic Symbol objects provide alternative definitions for all of the essential internal methods.

You can see them used at section 8.1.7.4 Well-Known Symbols and Intrinsics for example. For proposed uses (and still existing problems / open questions) of Symbol constructors have a look at these strawman pages or this wiki site.

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

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])

Why Object.keys is returns array of string instead of array of Numbers

Because Object.keys returns an array with strings

Object.keys() returns an array whose elements are strings corresponding to the enumerable properties found directly upon object. The ordering of the properties is the same as that given by looping over the properties of the object manually.

You get an array of strings, because Property names are strings by definition.

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.

Object.keys() array with one element treated as string?

Object.keys returns an array.

All objects have a toString method.

If you use an object in string context, toString is called implicitly.

Square-bracket property accessor notation is, essentially, string context (unless you pass a Symbol)

The default toString method on an array looks something like: function () { return this.join(","); }

const example1 = ["foo", "bar"];console.log("" + example1);
const example2 = ["baz"];console.log("" + example2);

Object.keys using numbers in typescript

All keys are strings in JavaScript - just use map:

const sizes: number[] = Object.keys(foo).map(Number);

This'll only work for numeric strings - if it's a European string involving decimals, for instance, it'll be NaN, and that never ends well.

console.log(Number("10,7"));


Related Topics



Leave a reply



Submit