2012-11-27

Coercing objects to primitives

This blog post looks at how JavaScript coerces objects to primitives. If you don’t know the difference between primitive values and objects, I suggest you consult my article “Categorizing values in JavaScript” at the Adobe Developer Connection. This post was triggered by the following tweet by David Bruant:
!!(new Boolean(false)) #wtfjs
The result of the above expression is true. Let us first learn about coercion in JavaScript. We can then use that knowledge to understand this result.

Coercion

Many operators and functions in JavaScript expect their arguments to have certain types. If they don’t, they are coerced (converted) to those types. Coercing an object to a primitive type is a two-step process: First, the object is converted to a primitive. Then, if necessary, the primitive is converted to the correct type. Two methods are used to convert an object to a primitive:
  • valueOf()
  • toString()
There are three conversion algorithms:
  • “Number”: you expect the value to be a number.
  • “String”: you expect the value to be a string.
  • “Default”: you don’t have any expectations for the value.
The number algorithm first calls valueOf() and uses the returned value if it is primitive. Otherwise, it calls toString() and uses its value if it is primitive. Otherwise, an exception is thrown. The string algorithm calls the methods in reverse order. The default algorithm is “number” for non-dates and “string” for dates.

Let’s try out coercion via the following object:

    var obj = {
        valueOf: function () {
            console.log("valueOf");
            return '0';
        },
        toString: function () {
            console.log("toString");
            return 1;
        }
    };

Coercing to number

There are two common ways for coercing to number: the unary plus operator and Number, used as a function (not as a constructor).
    > +obj
    valueOf
    0
    > Number(obj)
    valueOf
    0
In both cases, things work as expected: the number algorithm is used. Then the result returned by valueOf() is converted to number.

Coercing to string

Two common ways of coercing a value to string are: the binary plus operator where one operand is a string and String, used as a function (not as a constructor).
    > ''+obj
    valueOf
    '0'
    > String(obj)
    toString
    '1'
The binary plus operator uses the default algorithm, because one can add either numbers or strings.

Coercing to boolean

Two ways of coercing to boolean are: using the unary negation operator twice (once converts to boolean and negates) or using Boolean as a function.
    > !!obj
    true
    > Boolean(obj)
    true
Here we see that objects are never converted to primitive. The rule is simply: any object is always true. For primitives, only the following values are coerced to false, all other values are coerced to true.
  • undefined
  • null
  • false
  • +0, -0, NaN
  • ""

Understanding the initial result

Now it should be obvious why !!(new Boolean(false)) evaluates to true: Any instance of Boolean is always an object and those are always coerced to true.

Recommendations

Here are a few recommendations for coercion and objects:
  • Stay away from instances of Boolean, Number and String. You don’t normally need or encounter them in JavaScript.
  • However, I do like using Boolean, Number and String as functions, to coerce values. They are nicely descriptive when used in this manner.
  • Obviously, all of the above ways of coercing to primitives work for any value, not just for objects:
        > Number("123")
        123
        > Boolean(0)
        false
        > String(true)
        'true'
    
  • One does not often coerce objects to primitives. Doing so is, however, good for many WTFs [1] and hacks [2].

Further reading

  1. What is {} + {} in JavaScript? [Describes the binary plus operator and the conversion to number and string in detail]
  2. Fake operator overloading in JavaScript [a fun hack involving objects being coerced to numbers]
  3. JavaScript’s two zeros

No comments: