JS boolean arrays

2010-02-16

So Brian Leroux released his WTFJS. An interesting collection of oddities concerning Javascript.

Now it so happens to be I'm working on a similar project, going through the spec trying to find more of these oddities.

One of them, not (yet) listed there, has to do with arrays. As some/most (?) js people know, empty arrays can eval to false. Observe:

Code: (JS)
debug([1] == false); // false
debug((!![]) == ([] == true)); // false
debug([] == false); // true
debug(!![]); // true


So, !![] is true but when comparing it to false, that's also true... What's going on? :)

In this explanation I'll refer to the Ecmascript 5 specification. The numbers (10.3.1) refer to specific chapters of it for easy referencing.

First, the easy case. The exclamation mark simply takes the boolean value of its operand and returns the invert value of that result. As one would expect. Also, the boolean value of any object (not primitive!) is always true.

So, then why does []==false evaluate to true? It's all due to the behavior of the comparison operator, ==. According to the spec the operator follows some complex coercion rules (11.9.1, 11.9.3).

If the type of both operands are equal, no coercion happens. No problem.
The case of either operand being null or undefined does not apply here.
Then it tests whether one is string and the other number, which fails too.

The next case is when either operand is boolean. In that case the bool is converted to number using the abstract ToNumber (9.3). This results in a 0 or 1, depending on the value (+0 in this case).

The equality is applied again, this time it trickles down to almost the last rule. This says the object is converted to a primitive using ToPrimitive when the other value is a number or string (and the other value is +0 now).

ToPrimitive on an object is actually the [[DefaultValue]] internal method (8.12.8), which is a method which is not actually accessible through javascript but is used to explain how things work behind the scenes. It tries to get the toString method of the object. When callable (which is the case), the returned value is returned, but only if that's actually a primitive value itself (also the case).

Array.prototype.toString (15.4.4.2) simply calls the join method (15.4.4.5) of the array without a separator and returns the resulting string. In the case of an empty array, that's an empty string. Back to ToNumber.

The empty string is now converted to a number (recursive call) and will be converted according to the 9.3.1 algorithm. This has a production StringNumericLiteral which has a reading with no elements. This would match the empty string. Then later it says that the mathematical value of StringNumericLiteral::[empty] is 0. Finally, a number.

Now from there it's not so difficult to see why that equals to false in the end. ToNumber returns 0 to == which reapplies itself. This time the types are equal (both Number). Rule 1.c.iv applies and the operator finally returns true. Eureka!

So. Now you know why. I really hope it helps ya :)

--
A simple fix by @edwinm would be

Code: (JS)
Array.prototype.toString = function(){return true}

:)