Introduction to Coercion in Javascript

How many times have you wondered why 1 + "1" = "11" but 1 - "1" = 0 in Javascript?

How many times have you labeled Javascript as "weird" or "difficult"?

I suppose too many.

Let's demystify this.

This confusion mainly occurs because developers have an existing understanding of types in another language that they try to apply in Javascript.

Let's start understanding types in Javascript. For that, we would have to start by introducing ourselves to the official documentation of Javascript. And surprisingly it is not the MDN docs.

Javascript was originally called ECMAScript. Hence, the official documentation of JS can be found here: - ECMA-262

As of writing this article, the above-mentioned is the latest edition of the documentation. Feel free to search for the newer versions once available.

The topic we will be understanding today is one of the core features of JS and hence is very unlikely to change.

We first need to understand a term called Coercion. Coercion simply refers to implicit typecasting. Which can also be rephrased as forceful type conversion. But how does JS achieve this conversion? JS uses coercion in many places unknown to developers.

JS achieves it by using Abstract Operations. If you are somewhat familiar with programming, you might have heard of operations like addition, subtraction, multiplication, increment and decrement and many more like these. Then what's so different about Abstract Operations? Simply put, as the name itself suggests, these operations are abstracted from the developer. JS wants to do some operations under the hood, which we can also know about in the documentation.

You can search for Abstract Operations in the documentation or here is a reference to the web version of the same.

As per the documentation, Abstract Operations are in the form of methods.

Here are some of the Abstract Operations:-

  1. ToNumber

  2. ToPrimitive

  3. ToBoolean

  4. ToString(not to be confused with the method toString())

Do those method names ring a bell? They all sound like they would convert some given input to a number/string/boolean/primitive. Which is nothing but coercion.

Let's start with the first example: -

Output for the above code snippet is: -

This happens because of a series of fixed steps that JS executes underneath, let's explore those steps!

We begin by exploring the addition operator page.

It gives a note followed by the evaluation details section which has a link to an Abstract Operation. Let's see what that abstract operation does,

It takes a left operand and a right operand along with the operator(+). Let's ignore the steps 1 and 3. But in steps 2 and 4, we just get the value of the operands and store them in lval and rval. Then we call another abstract operation, let's see what does that do: -

It takes the same arguments as the previous one. But in the execution steps, notice point 1. If the operator is '+', then execute some steps. In those steps notice the step c. It clearly says if any one of the operands is a string, convert both the operands to a string by calling the ToString abstract operation.

As in the example we have used integers(referred to as Numbers in the docs), so we can jump to step 7. We now go to Number:toString.

In the Number:toString algorithm, the initial steps deal with 0 and Infinity values and their negations. For now let's focus on step 12 that is relevant to our example: -

So, that gives us the string representation of a numeric operand i.e. "1" -> 1. The second operand in our example is already in string form. We take the results and go back to ApplyStringOrNumericBinaryOperator abstract operation which had the following step where we started from

As we have both the operands coerced to a string, we just return the string concatenation of them. And this is how 1 + "1" becomes "11".

Now, let's look at why 1 - "1" = 0 in JS.

Output for this code snippet is: -

Just like the previous example, we will start by looking at the operator being used i.e. in this case Subtraction.

Just like the addition operator, it calls the abstract operation EvaluateStringOrNumericBinaryExpression. Which then calls another one which we have again seen in the addition operation.

We then call ApplyStringOrNumericBinaryOperator which has the steps we will be executing.

Earlier while learning about the addition operation, we went to step 1 as our operator or opText was '+'. But now it is '-', hence we look at the other steps. Notice that the string conversion does not happen in case the operator is not '+'. In step 2 we are assured that it is a numeric operation(-). In steps 3 and 4 we convert our operands to numeric type, here is how that's done.

Remember that ToNumeric and then ToNumber will be called one after the other two times as we have two operands.

ToNumeric calls ToNumber at step 3. Hence, we jump to that operation. We will explore step 1 i.e. ToPrimitive in another article.

For now, let's focus on step 3 of ToNumeric which takes us to Step 1 of ToNumber as we have considered a number and a string in our example 1 - "1" = 0. Step 1 simply returns the same value for the first operand, but for the second one as the type is String we can directly go to step 6. In step 6 we convert the argument(operand) of type String to a Number using another abstract operation StringToNumber. What does that do? Let's find out

This algorithm tries to convert a string literal e.g "1" to a number. If the conversion fails it returns NaN which is an invalid number. Any operation with NaN will result in NaN only. We will cover NaN in another article. In our case conversion of "11" will be successful and hence the result will be 11.

Now we go back to ApplyStringOrNumericBinaryOperator

Here, now that we have executed steps 3 and 4, we can go to step 7 as in step 5 our types are the same so that will be skipped, and step 6 will also be skipped as none of the operands in our example are Big Integers.

In step 7, based on our operator and the type of the first operand in the subtraction operator, we go to the underlined step. The operation to be executed is Number: subtract. Which has the following steps it executes: -

Now that we know that both the arguments to this function are Numbers and are the operands to our subtraction operation, we do (x + (-y)) which is an atomic operation. So our example now executes as 1 + (-1) = 0. This is how using the subtraction operator performs a numeric operation. The deciding factor compared to the addition operation was the ToNumeric conversion that was performed on both the operands.

That was the deciding factor between the outputs "11" of addition and 0 in subtraction. That was an abstract operation and not something "weird" or "difficult" :)

This is how JS can differentiate between the two operands '+' and '-'. It follows a fixed set of steps which if known, can be used to predict the output with 100% accuracy.

You might have questions regarding whatever we have learned just now. Feel free to drop them in the comments below.

Also, as a novice writer, I might have committed some mistakes, and I would love to correct them based on your suggestions. Feel free to drop them too in the comments below.