I got in a brief conversation in the node.js IRC channel today about how weird type conversion is in JavaScript. Specifically, this code was suspect:
What this shows is that when a Boolean object is used in an if
statement, it always evaluates to true
, but when it’s used in a type coercing operator (such as ==
), it converts to the value it represents. Why?
The reason is actually pretty straightforward, even though it’s not obvious when looking at it. if
statements convert the result of their expression to a boolean primitive, whereas ==
converts the right side expression into the type of the left side and checks for equivalency. So if
statements don’t do type conversion at all until the expression is evaluated, and then it implicitly converts the result of that expression into a boolean primitive.
JavaScript is really just doing this:
Where the weirdness comes in is how type converting an object works. If you look at the JavaScript spec, you can see that converting an Object to a boolean is always true
. And guess what? Instances of Boolean
are objects:
Since new Boolean
instances are objects, they will always be true
when converted to a boolean primitive, which is exactly what the if
statement is doing.
So then, why does new Boolean(false) == false
result in true
? Well it’s because the rules are different for ==
. Let’s say you’re doing false == new Boolean(false)
. This will result in true
because the left side of the expression is a primitive, so the interpreter will convert the Boolean instance, not to a boolean, but to a primitive. When converting an object to a value, the interpreter will:
Return a default value for the Object. The default value of an object is retrieved by calling the internal [[DefaultValue]] method of the object, passing the optional hint PreferredType. The behaviour of the [[DefaultValue]] method is defined by this specification for all native ECMAScript objects (8.6.2.6).
And, you guessed it, the default value of a Boolean instance is just the boolean primitive that it represents. So, after conversion, we’re testing false
against false
which is of course true.
The other direction also works. If we’re doing (new Boolean(false)) == false
, then the interpreter will convert false
to an Object
. The spec specifies that to convert a boolean primitive to an object, it should:
Create a new Boolean object whose [[value]] property is set to the value of the boolean.
So then we have two equivalent Boolean instances being compared, which again results in true
Pretty crazy, but it kind of makes sense when you think about it. Maybe.