Monday, March 1, 2010

Equal but different JavaScript style

Someone at work pointed out this surprise in JavaScript: [] == ![] evaluates to true.

Reading this I thought it was stating something is equal to the opposite of itself.  For non technical readers can things be equal without being the same?  Currencies?  People?  In English do people sometimes say same but mean equal?

In programming worlds things which are not the same might be equal, but I expect something that is the opposite of itself to definitely not be equal to itself.  For example 1 is not equal to !1  I decided to discover how can this be?

I started by firing up a debugger and looking at the types [] and ![].  ![] is false*.

Then I went to the ECMA-262 spec:

Section 11.9.1 The Equals Operator (==) points us to 11.9.3 abstract equality comparison.

11.9.3 The Abstract Equality Comparison Algorithm
The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:
7.If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

9.3 ToNumber
  Given Argument Type Boolean The result is 1 if the argument is true. The result is +0 if the argument is false.

Going back to 11.9.3

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

Section 9.1 ToPrimitive:
For input type 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.

Section 8.12.8 DefaultValue

When the [[DefaultValue]] internal method of O is called with hint Number, the following steps are taken:

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

valueOf defined in Section 15.2.4.4 does not return a primitive so toString() from step 3 is called resulting in ""

Going back to 11.9.3

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

Section 9.3.1 ToNumber Applied to the String Type informs us that whitespace returns 0.

The leaves us with ([].toString() == "") == (![] == 0) which can be confirmed in a debugger and also results in [] == ![] resulting in true.



*To know why ![] is false
See section 11.4.9

11.4.9 Logical NOT Operator ( ! )
The production UnaryExpression : ! UnaryExpression is evaluated as follows:
1.Let expr be the result of evaluating UnaryExpression.
2.Let oldValue be ToBoolean(GetValue(expr)).
3.If oldValue is true, return false.
4.Return true.

Step 2 ToBoolean(GetValue(expr))
Takes us to Section 9.2 ToBoolean where: for Argument Type Object the result is true.

3 comments:

Anonymous said...

Of course, what a great site and informative posts, I will add backlink - bookmark this site? Regards, Reader.

Anonymous said...

I'm glad you said that...

Anonymous said...

I am glad you said that?!

Kindest Regards
Alfredo