Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript apparent madness [duplicate]

Possible Duplicate:
Conflicting boolean values of an empty JavaScript array

What is the rationale behind the fact that

[ ([] == false), ([] ? 1 : 2) ]

returns [true, 1]?

In other words an empty list is logically true in a boolean context, but is equal to false.

I know that using === solves the issue, but what is the explanation behind this apparently totally illogical choice?

In other words is this considered a mistake in the language, something unintentional that just happened and that cannot be fixed because it's too late or really in the design of the language someone thought it was cool to have this kind of apparent madness that I'm sure is quite confusing for many programmers?

The technical explanation of how this happens is at the same time amazing and scaring, I was however more interested in what is behind this design.

Edit

I accepted very detailed Nick Retallack explanation, even if it's only about the technical reasons of why []==false is true: surprisingly enough it happens is because [] converted to string is an empty string and the empty string numeric value is special cased to be 0 instead of the apparently more logical NaN. With an empty object for example the comparison ({}) == false returns false because the string representation of an empty object is not the empty string.

My curiosity still remains about all this being just unanticipated (and now unfortunately solidified in a standard).

like image 616
6502 Avatar asked May 11 '12 17:05

6502


2 Answers

The confusion here is around the definition of "falsy" in JavaScript, which (contrary to popular belief) isn't the same as == false.

Falsy actually refers to a value that has a boolean equivalent of false, not an expression whose result == false. The only Falsy values in JavaScript are: false, 0, "", null, undefined, and NaN. So any of those values -- or any expression that evalutes to one of those values (like in an if statment or using the ternary operator) -- is falsy.

Here's a table I put together of falsy/truthy values in JavaScript that should help explain this whole issue. http://jsfiddle.net/philipwalton/QjSYG/

like image 178
Philip Walton Avatar answered Oct 17 '22 00:10

Philip Walton


Let's get technical. I'll explain the logic with quotes from the ECMAScript Standard 262.

The expression [] ? 1 : 2 is very simple:

11.12 Conditional Operator ( ? : )

  • Let lref be the result of evaluating LogicalORExpression.
  • If ToBoolean(GetValue(lref)) is true, then
    • Let trueRef be the result of evaluating the first AssignmentExpression.
    • Return GetValue(trueRef).
  • Else
    • Let falseRef be the result of evaluating the second AssignmentExpression.
    • Return GetValue(falseRef)

9.2 ToBoolean

  • Undefined: false
  • Null: false
  • Boolean: The result equals the input argument (no conversion).
  • Number: The result is false if the argument is +0, 0, or NaN; otherwise the result is true.
  • String: The result is false if the argument is the empty String (its length is zero); otherwise the result is true.
  • Object: true

So it's true.


Now for the wild ride of what happens when you use the double equals operator. Perhaps this will help explain why you should never do this.

The behavior of == is explained in in section 11.9.3: The Abstract Equality Comparison Algorithm.

For x == y where x = [] and y = false, this happens:

11.9.3: The Abstract Equality Comparison Algorithm

If Type(y) is Boolean, return the result of the comparison x == ToNumber(y)

9.3 ToNumber

The result is +0 if the argument is false.

Now we have [] == 0

11.9.3: The Abstract Equality Comparison Algorithm

If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.

9.1 ToPrimitive

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.

8.12.8 DefaultValue:

When the [[DefaultValue]] internal method of O is called with no hint, then it behaves as if the hint were Number

  • Let valueOf be the result of calling the [[Get]] internal method of object O with argument "valueOf".
  • If IsCallable(valueOf) is true then,
    • Let val be the result of calling the [[Call]] internal method of valueOf, with O as the this value and an empty argument list.
    • If val is a primitive value, return val
  • Let toString be the result of calling the [[Get]] internal method of object O with argument "toString".
  • If IsCallable(toString) is true then,
    • Let str be the result of calling the [[Call]] internal method of toString, with O as the this value and an empty argument list.
    • If str is a primitive value, return str.

I assume this first attempts valueOf and then rejects it because the result is the same array you started with. It then calls toString on Array, which seems to be universally implemented as a comma separated list of its values. For empty arrays like this one, that results in empty string.

Now we have '' == 0

11.9.3: The Abstract Equality Comparison Algorithm

If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y

9.3.1 ToNumber Applied to the String Type

A StringNumericLiteral that is empty or contains only white space is converted to +0.

Now we have 0 == 0

11.9.3: The Abstract Equality Comparison Algorithm

If x is the same Number value as y, return true

Awesome. It's true. Pretty convoluted way of getting here though.

like image 8
Nick Retallack Avatar answered Oct 17 '22 00:10

Nick Retallack