16 Oct 2016 - by 'Maurits van der Schee'
What does the following expressions evaluate to?
>  &&  == false true
Yes, it is "true"! If you don't believe me, try it. The question that is addressed in the remainder of this post is: "Really? Why?!"
> (?true:false) true > Boolean() true
Let's test a few script languages to see what they evaluate empty array to, when used as a boolean expression:
$ nodejs -e 'console.log( ? "true" : "false")' true $ php -r 'print ( ? "true" : "false");' false $ perl -e 'print ( ? "true" : "false")' true $ perl -e 'print (() ? "true" : "false")' false $ ruby -e 'print ( ? "true" : "false")' true $ ruby -e 'print (() ? "true" : "false")' false $ python -c 'print ("true" if  else "false")' false
As you can see the empty array in Perl and Ruby also evaluates to "true", while empty lists evaluate to "false". So all languages choose the (in my opinion) more logical "false" value to evaluate to (in one way or another). When we replace the empty array with "true" the expression behaves as expected:
> true && true == false false
It almost seems as if in this case the empty array is not evaluated to "true". That can't be the case or can it?
I tried the following to find out whether or not this had to do with operator precedence:
> ( && ) == false true >  && ( == false) true
But since both evaluate to true, I think we can rule operator precedence out. We have to dig a little deeper.
I got the feeling it had something to do with the way script languages evaluate logical expressions.
$ nodejs -e 'console.log((2 && 3) > 2)' true $ php -r 'var_export((2 && 3) > 2);' false $ perl -e 'print ((2 && 3) > 2)' 1 $ ruby -e 'print ((2 && 3) > 2)' true $ python -c 'print ((2 and 3) > 2)' True
Instead of returning true or false most scripting languages (PHP excluded) return the first "falsy" (evaluates to false) or the second "truthy" (evaluates to true) argument of the "&&" or "and" expression. You can see that here in more detail:
> 0 && 1 0 > 2 && 3 3
Now let's see whether or not logical operator evaluation matters by replacing the "" (empty array) by a "1" (one), another non-boolean that also evaluates to "true":
> Boolean(1) true > 1 && 1 == false false
So somehow if we replace "" (empty array) with "1" (one) we get correct results. Since "1" (one) does not show the (strange) behavior that the empty array shows, the behavior seems to be unrelated to the way logical operators are evaluated.
So, let's approach it from the other side now. The only way we can construct the result "true" is when the "==" (comparison) operator fails to convert correctly to the type of the left or right hand side of the comparison expression. Let's try all possible paths:
> Boolean()==Boolean(false) false > Number()==Number(false) true > String()==String(false) false > Object()==Object(false) false
You would expect the first to happen (or the last), because that seems the logical way to convert both sides to the same type. Apparently this is not true and the comparison expression between an object and a boolean is evaluated as a numerical comparison (a type that is unrelated to both the left and the right side). Don't you believe me? Read on.
Let's see what the EcmaScript 6 standard states on "Equality Comparison":
The comparison x == y [...] is performed as follows: [...] If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
It states (as the first matching step of the comparison algorithm) that comparison with a boolean on the right hand side should be done by converting that boolean to a number, just like we expected from our analysis.
> Boolean() true >  == false true
Now that I studied the standard I was able to come up with a similar case. Can you now reason why this one evaluates to "true"?
> "0" == !"0" true