Search code examples
javascriptarrayscoercion

What is happening in this loose equality comparison of 2 empty arrays


I am struggling with understanding how this snippet works on a basic level

if([] == ![]){
    console.log("this evaluates to true");
}

Please help me understand where I got it wrong. My thinking:

  1. First there is operator precedence so ! evaluates before ==.
  2. Next ToPrimitive is called and [] converts to empty string.
  3. ! operator notices that it needs to convert "" into boolean so it takes that value and makes it into false then negates into true.
  4. == prefers to compare numbers so in my thinking true makes 1 and [] is converted into "" and then 0

Why does it work then? Where did I get it wrong?


Solution

  • Why does it work then?

    TLDR:

    [] == ![]
            //toBoolean [1]
    [] == !true
    [] == false
    //loose equality round one [2]
    //toPrimitive([]); toNumber(false) [3]
    "" == 0
    //loose equality round two
    //toNumber("") [4]
    0 === 0
    true
    

    Some explanations:

    1)

    First there is operator precedence so ! evaluates before ==

    Negating something calls the internal toBoolean method onto that "something" first. In this case this is an Object (as Arrays are Objects) and for that it always returns true which is then negated.

    2)

    Now it's up to loose equalities special behaviour ( see Taurus answer for more info) :

    If A is an Object ( Arrays are Objects ) and B is a Boolean it will do:

    ToPrimitive(A) == ToNumber(B)
    

    3)

      toPrimitive([])
    

    ToPrimitive(A) attempts to convert its Object argument to a primitive value, by attempting to invoke varying sequences of A.toString and A.valueOf methods on A.

    Converting an Array to its primitive is done by calling toString ( as they don't have a valueOf method) which is basically join(",").

    toNumber(false)
    

    The result is 1 if the argument is true. The result is +0 if the argument is false. Reference

    So false is converted to +0

    4)

    toNumber("")
    

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

    So finally "" is converted to +0

    Where did I get it wrong?

    At step 1. Negating something does not call toPrimitive but toBoolean ...