- Published on
Weird JS Array
- Authors
- Name
- Cookie
Recently, I updated a form field in my job, changing its input type from radio
to checkbox
to allow the user to deselect the option. This also meant the field's value changed from a string to an array containing a single string element, for consistency with other checkbox fields.
Since our i18n
system uses a key-mapping approach for translations, I expected the change to break the translation. However, I was surprised to find that it still worked correctly. Curious about what was happening during the key lookup in an object, I decided to investigate further.
My first hypothesis was that the array was implicitly converted to a string during the key lookup. To test this, I intercepted the get
method using a Proxy
to log any properties accessed on the array:
const array = new Proxy([], {
get: function (target, prop, receiver) {
console.log('Getting: ', prop)
return Reflect.get(target, prop, receiver)
},
})
Then, I create an object with predefined keys and attempted a lookup using the array:
const obj = {
a: 'Hello',
'a,b': 'Hello World.',
}
console.log(obj[array])
// Console output:
// Getting: , Symbol(Symbol.toPrimitive)
// Getting: , toString
// Getting: , join
// Getting: , length
// undefined
As shown, the toString()
method is indeed called, confirming my initial assumption. To further verify, I added two elements to the array:
array.push('a')
console.log(obj[array]) // 'Hello'
array.push('b')
console.log(obj[array]) // 'Hello World.
When using non-string types as object keys, JavaScript implicitly converts them to string.1 Therefore, you can use anything to index an object in JavaScript as long as it has a toString
method.
obj[true]
function foo() {}
obj[foo]
const sym = Symbol('foo')
obj[sym]
All primitive types, except null
and undefined
, have their corresponding object wrappers. When a property is accessed on a primitive value, JavaScript automatically wraps the value into the corresponding wrapper object and accesses the toString
property on the object instead.2
In the console output we also see Symbol(Symbol.toPrimitive)
. This method allows objects to specify the returned primitive representation based on a provided hint
(an argument indicating the preferred type), and it's called before falling back to the toString
method.3
Footnotes
JavaScript data types and data structures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#properties ↩
JavaScript data types and data structures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values ↩
Symbol.toPrimitive: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive ↩