[1] + [2] – [3] === 9!? Looking into assembly code of coercion.

JavaScript

Variable values have certain types. In fact, you can cast a value of one type to the other. If you do it explicitly, it is type casting (also called explicit coercion). If it happens in the background when you are trying to perform an operation on types that do not match, it is called coercion (sometimes referred to as implicit coercion). In this article, I will walk you through both, so that you can better understand the process. Let’s dig in!

Type casting

Primitive types wrappers

As I described in one of my previous articles, almost all primitive types in JavaScript (besides null and undefined) have object wrappers around their native value. In fact, you have access to their constructors. You can use that knowledge to convert the type of one value to another.

The wrapper for that particular variable of primitive type is not kept for long though: as soon as the work is done, it is gone.

You need to watch out for that because if you use a new keyword there, this is not the case.

Since bool is a new object here (not a primitive value), it evaluates to true.

I will even go a little further and tell you that

is actually the same as doing

Don’t believe me, try it yourself! Bear with me, I will use Bash here.

  1. Compile the code into the assembly using node.js
  2. Prepare a script to compare the 4th column (assembly operands) – I intentionally skip memory addresses here, because they might differ.
  3. Run it

parseFloat

This function works similar to Number constructor but is less strict when it comes to the argument passed. If it encounters a character that can’t be a part of the number it returns a value up to that point and ignores the rest of characters.

parseInt

It rounds the number down while parsing it. It can work with different radixes.

Function parseInt can either guess the radix or have it passed as a second argument. For a list of rules it takes into consideration, check out MDN web docs.

It has troubles with very big numbers, so it should not be considered an alternative to Math.floor (which will also do a typecast):

toString

You can convert values to strings using a toString function. Implementation of this function differs between prototypes.

If you feel like you’d like to grasp the concept of the prototype better first, feel free to check out my other article: Prototype. The big bro behind ES6 class.

String.prototype.toString
returns a value of a string

Number.prototype.toString
returns a number converted to String (you can pass appendix as a first argument)

Symbol.prototype.toString
returns  `Symbol(${description})`

If you are lost here: I’m using a concept of template literals as a way to explain for you how the output strings look.

Boolean.prototype.toString
returns “true” or “false”

Object.prototype.toString
Objects have internal value called [[Class]]. It is a tag that represents the type of an object. Object.prototype.toString returns a string `[object ${tag}]` . Either it is one of the built-in tags (for example “Array”, “String”, “Object”, “Date”), or it is set explicitly.

With the introduction of ES6, setting tags is done with the usage of Symbols.

You can also use ES6 class with a getter here:

Array.prototype.toString
calls toString on every element and returns a string with all the outputs separated by commas.

Coercion

If you have a knowledge of how type casting works, it will be a lot easier for you to understand coercion.

Mathematical operators

Plus sign

Expression with two operands and with  +  that involves a string will result in a string.

You can use it with one operand to cast it to a number:

Other mathematical operators

With other mathematical operators such as - or / operands will always be cast to numbers.

Date, cast to a number gives a Unix timestamp.

Exclamation mark

Using it will output true if the original value is falsy, and false if it is truthy. Therefore, it can be used to cast the value to corresponding boolean if used twice.

ToInt32 with bitwise OR

It is worth mentioning, even though ToInt32 is, in fact, an abstract operation (internal-only, not callable). It will cast a value to a signed 32-bit integer.

Performing a bitwise OR operation when one of the operands is 0 will result in not changing the value of the other operand.

Other cases of coercion

While coding, you may encounter more situations in which values will be coerced. Consider this example:

This happens because both foo and bar, when cast to strings, result in  "[object Object]" . What really happens is this:

Coercing also happens with template literals. Try overriding toString function here:

Coercion is also a reason why abstract equality comparison (==) might be considered a bad practice since it is attempting to coerce values if their types don’t match.

Check out this example for an interesting fact about the comparison:

Because we used the new keyword here, foo and foo2 both preserved wrappers around their native value (which is ‘foo‘). Since they are referencing to two different objects now,  foo === foo2 will result in false. Relational operators ( >= here) call the valueOf function on both operands. Due to that, the comparison of native values is taking place, and  'foo' >= 'foo' evaluates to true.

[1] + [2] – [3] === 9

I hope all that knowledge helped you to demystify the equation from the title of this article. Let’s debunk it anyway!

  1. [1] + [2] these are cast to strings applying the rules of Array.prototype.toString and then concatenated. The result will be  "12" .
    • [1,2] + [3,4]  would result in  "1,23,4"
  2. 12 - [3] will result in subtracting  "3"  from  12 giving us  9
    • 12 - [3,4] would result in NaN because  "3,4" can’t be cast to a number

Summary

Even though many may advise you to just avoid coercion, I think it is important to understand how it works. It might not be a good idea to rely on it, but it will help you greatly both in debugging your code and avoiding the bugs in the first place.

Comments (5)

  1. Interesting writeup… There are some things that may feel weird… but in the end it makes sense when you’re thinking of ETL and validation. In the case of the above, it’s a garbage in/garbage out scenario… the fact that JS lets you do it is pretty awesome. JS is better than most at dealing with garbage and not blowing up. For it’s first purpose, validating input, it’s great. It also makes it great as a middle-tier service language as a result.

  2. “It has troubles with very big numbers, […]

    parseInt(‘1.261e7’); // 1

    No: this is not due to the 1.261e7 being large. It’s because it contains a dot which an integer can not. So the parsing stops after 1 and the result is 1—same with the “not so large” number 1.261e2.

  3. Awesome man! You just got a new follower 😉 There is a typo in your article. Plus Sign first comment in example: “// ’22’ “.

Leave a Reply

Your email address will not be published. Required fields are marked *