2011-04-17

JavaScript: converting any value to an object

This post is about converting between primitive values and wrapper objects [1]. Thankfully, one is usually not faced with this kind of task in JavaScript. The most frequent use case is to add properties to a primitive. As a primitive is immutable, you need to convert it to a wrapper object to do so. Read on if you want to see some of JavaScript’s more obscure corners.

Let us start with a short quiz:
What does ({}).valueOf.call(myvar) do?
Short answer: it converts any value to an object (an object remains unchanged, a primitive is converted to an instance of a wrapper type). The longer answer is as follows. The section numbers refer to the ECMAScript 5 specification (ECMA-262, 5th edition).
  • ({}).valueOf uses an instance of Object to access Object.prototype.valueOf.
  • The call() method sets this to myvar and invokes Object.prototype.valueOf, without any (explicit) parameters.
  • Object.prototype.valueOf (ECMA-262, 15.2.4.4) invokes the internal abstract operation ToObject (ECMA-262, 9.9). This operation converts a primitive to a (wrapper) object and leaves an object untouched. Thus, given a value, you always end up with an object.
This is a bit illogical, because in all subtypes of Object, valueOf() is about converting from a wrapper to a primitive (i.e., the opposite direction).
    > String.prototype.valueOf.call(new String("abc"))
    'abc'
    > String.prototype.valueOf.call("abc")
    'abc'
    > "abc".valueOf()
    'abc' // via String.prototype.valueOf()

    > Object.prototype.valueOf.call("abc")
    { '0': 'a'
    , '1': 'b'
    , '2': 'c'
    }
    > Object.prototype.valueOf.call(new String("abc"))
    { '0': 'a'
    , '1': 'b'
    , '2': 'c'
    }
So, Object.prototype.valueOf.call() is verbose and not very intuitive for converting values to objects. A more descriptive alternative is the function Object() (emphasis below is mine).
When Object is called as a function rather than as a constructor, it performs a type conversion [to an object]. [ECMA-262, 15.2.1]
Examples:
    > Object("abc") instanceof String
    true
    > Object(new String("abc")) instanceof String
    true
    > Object(null)
    {}
Using Object as a constructor (with new) basically has the same effect, but as a function, it better expresses the fact that there isn’t always a new object being created.

Related reading:
  1. JavaScript values: not everything is an object
Flattr

No comments: