Exactly why does the condition here evaluate to true?
var condition = new Boolean(false);
if (condition == !condition)
alert("The more you know...");
Exactly why does the condition here evaluate to true?
var condition = new Boolean(false);
if (condition == !condition)
alert("The more you know...");
Break it down:
var condition = new Boolean(false);
This is actually an object, and condition.valueOf() === false
!{}
evaluates to false since {}
is true (explained http://www.ecma-international.org/ecma-262/5.1/#sec-9.2)
So the check is condition.valueOf() == false
, which is true
You're comparing an object (LHS) to the boolean false
(RHS).
[object Boolean] == false
The ==
operator performs type coercion according to the Abstract Equality Comparison Algorithm defined by ECMAScript. 11.9.3 The Abstract Equality Comparison Algorithm
Relevant to your code is the following point of that algorithm (where x is the LHS and y is the RHS).
7) If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
Notice that it actually first attempts to convert the boolean to a number. The false
boolean converts to the number 0
, so now we have this:
[object Boolean] == 0
As you can see, it recursively enters the same algorithm because of the ==
. So now we're comparing an object to a number and so the following point applies:
9) If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.
So here it's now attempting to coerce the object to its primitive value. From 9.1 ToPrimitive, when called on an Object:
Object Return a default value for the Object. The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType. The behaviour of the [[DefaultValue]] internal method is defined by this specification for all native ECMAScript objects in 8.12.8.
So you can see that it wants the [[DefaultValue]]
of the object. That brings us to 8.12.8 [[DefaultValue]], where it expects a "hint". Because it received no "hint", it behaves as though the hint was "Number".
When the [[DefaultValue]] internal method of O is called with no hint, then it behaves as if the hint were Number,...
And so that brings us to the following behavior:
When the [[DefaultValue]] internal method of O is called with hint Number, the following steps are taken:
Let valueOf be the result of calling the [[Get]] internal method of object O with argument "valueOf".
If IsCallable(valueOf) is true then,
a. Let val be the result of calling the [[Call]] internal method of valueOf, with O as the this value and an empty argument list.
b. If val is a primitive value, return val.
And so it calls the .valueOf()
method on the object, bringing us to 15.6.4.3 Boolean.prototype.valueOf ( )
Let B be the this value.
If Type(B) is Boolean, then let b be B.
Else if Type(B) is Object and the value of the [[Class]] internal property of B is "Boolean", then let b be the value of the [[PrimitiveValue]] internal property of B.
Else throw a TypeError exception.
Return b.
And so you can see from step 3 that it will return the [[PrimitiveValue]]
of the object. That brings us to 15.6.2.1 new Boolean (value)
The [[PrimitiveValue]] internal property of the newly constructed Boolean object is set to ToBoolean(value).
And so you can see that you'll finally get the ToBoolean
value of the value you originally passed to the constructor, which was false
. Its ToBoolean
value is obviously false
, so now your comparison is this:
false == 0
Since the types still don't match, it'll go to point 6 in the original algorithm:
6) If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
And so it now wants to convert the boolean false
to a number. This is similar to what was done above. The value false
converts to the value 0
, so now we have:
0 == 0
And finally we have a type matched comparison, and so it behaves the same as its strict ===
counterpart, by comparing values. And clearly, 0
does equal 0
, so we get true
.
Moral of the story... this is what you get when you ask "why" in JavaScript.
It really helps just to read the spec where this is logically laid out, see
Start with:
var x = new Boolean(false);
var y = !x;
Then
y = false;
Because a boolean object is not considered a boolean but an everday object, ToBoolean(x)
evaluates to true
and the !true
evaluates to false
The Abstract Equality Comparison Algorithm
Round 1: step 7
If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
y = 0; //ToNumber(false) is 0
Round 2: step 9
If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.
x = false //Basically ends up calling x.valueOf()
Round 3: Step 6
If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
x = 0; //ToNumber(false) is 0
Finally: Step 1
If Type(x) is the same as Type(y), then
Step 1 C
If Type(x) is Number, then
Step 1 C iii
If x is the same Number value as y, return true.
One thing I learned from The Abstract Equality Comparison Algorithm is, forget intuition, and follow the steps.
As objects are truthy, their negation is false
, so what goes into the algorithm is:
Boolean(false) == false
If you go step by step, you get:
Boolean(false) == 0 // Step 7
false == 0 // Step 9
0 == 0 // Step 6
true