DEV Community

Discussion on: So you think you know JavaScript?

 
milkysingh27 profile image
Malkeet • Edited

{} + [] = 0

Before we understand how did we get that strange result, we need to dive into how type conversion a.k.a "Coercion" works in JavaScript.

Abstract operations are the fundamental building blocks that makes up how we deal with coercion. Abstract Operation fundamentally performs the type conversion for us.

By the way, Abstract operations are not the functions which get called. By calling them abstract, we mean they’re conceptual operations. These operations are not a part of the ECMAScript language; they are defined here to solely to aid the specification of the semantics of the ECMAScript language.

In our case, we are trying to do a numeric operation with 2 non-primitive types i.e an object and an array. So, to perform this numeric operation JS needs to convert them into primitives first i.e Numbers in our case. So if we have something non-primitive, like one of the object types like an object, an array, and a function and we need to make it into a primitive, there is an Abstract method named ToPrimitive which is going to get involved in doing that.

So any time you have something that is not primitive and it needs to become primitive, conceptually, what we need to do is this set of algorithmic steps, and that's called ToPrimitive as if it were a function that could be invoked.

ToPrimitive

The ToPrimitive abstract operation takes an optional type hint. So it says like “If you have something which is not primitive, tell me what do you think you would like, what type you would want it to be ?” If you are doing a numeric operation and it invokes ToPrimitive, guess what’s hint it’s gonna send in? - A Number. That doesn’t guarantee a number but it’s just a hint to say that the place I’m using it requires it to be a number. There can basically be 2 types of hints that can be sent i.e number and string.

Another thing we need to understand is that the algorithms within JS are inherently recursive, which means that they define something, for example - ToPrimitive and if the returned result from ToPrimitive is not Primitive, then it's gonna get invoked again and it's gonna keep getting invoked until we get something that's an actual Primitive or in some cases an error.

How does ToPrimitive works ?

The way it works is that there are 2 methods that can be available on any non-primitive i.e valueOf() and toString(). This algorithm says, if you've told me that the hint is number, then I'm going to first try to invoke the valueOf() function if it's there, and see what it gives me. And if it gives me a primitive then we're done. If it doesn't give me a primitive, or it doesn't exist, then we try the toString(). If we try both of those and we don't get a primitive, generally that's gonna end up resulting in an error. That happens when the hint is number. If the hint was string, they just reverse the order that they consult them in.

When JS executes {}+[], the following things happen -

  • As we are doing a numeric operation and we need primitives, ToPrimitive method gets called with "number" as a hint when we try to convert [].

  • As we know ToPrimitive calls 2 methods i.e valueOf() and toString() in an order depending on the hint. In this case, the hint was a number so valueOf() function is invoked.

  • For an array or an object, by default, the valueOf method essentially returns itself. Which has the effect of just ignoring the valueOf and deferring to toString(). So it doesn't even matter that the hint was number. It just goes directly goes to *toString() *.

                          valueOf() {return this;}
    

Without further digression, let's answer the question []+{} = 0

  • When the parser sees that there is { at the beginning of a statement, it treats it as a block. In our case {} as no statements inside it hence it has empty continuation value. {} in our case is just an empty block which doesn't have any impact on the given statement.

                The given statement could be written as 
    
                             1. {}
                             2. +[]
    
               and it will produce the same result i.e 0.
    
  • Now coming down +[] statement, coercion kicks in here. [] is a non-primitive type which needs to be converted to Primitive and due to the + operator, it passes "number" as a hint to ToPrimitive which further calls valueOf() which defers it to toString() method.

                   It looks like this =>  String([]) = "";
    
  • "" is a primitive type which is returned from the ToPrimitive operation.

  • Now we have +"", again coercion kicks in and calls another abstract method ToNumber() which converts every non-numeric primitive to numeric primitive.

                       Number("") = 0; 
    
        In this case 0 is returned from ToNumber abstract method.
    
  • We are now left with +0 which is equals to 0.Here we have solved it, {} + [] = 0

Thread Thread
 
aman_singh profile image
Amandeep Singh

Thanks Malkeet for your time writing an in-depth explanation of how addition operators works. This comment can be turned into a single blog post. Would love to see that happen. ❤️❤️😍