You all know it, JavaScript has completely random behaviors that are impossible to predicts. But are those behaviors really that random?

Strange JavaScript results

Javascript vs NaN

Okay, several things about this one.

typeof NaN === 'number' // true
NaN === NaN // false

This one is pretty known.

First, why NaN (which stand for Not a Number) as the type Number? Let’s just think one second, an exemple of function that will return NaN is the Math.sqrt function when you give it a negative number, for exemple.

Math.srqt(-1) // NaN

It seems correct, JavaScript, just like almost any other langage, does not handle complex number natively. But honestly, did you really expect that the value used to represent that was not of type number? Some languages would have thrown an exception, JavaScript use NaN, but it does not seem absurd to return a value that is of type number nevertheless.

Now, why NaN is not equal to NaN? Well, asking for that is the same as asking for

Math.srqt(-1) === Math.sqrt(-2)

to be true. It is, definitely, not what you want.

Javascript vs double precision

9999999999999999 === 10000000000000000 // true

Ahah, so dumb, he cannot handle equality between integers. Go back to another langage that almost everyone knows: C.

In C, you have several types that can store numbers in two categories: integers (char, short, int, long and long long) and floating point numbers (float and double). Now, just assign those two numbers to doubles in C, and then check for equality.

You will get the same result, because the type Number in JavaScript is in fact a double. Double allow to store floating point numbers, as well as integer on 51 bits. If the integer is to large for 51 bits, it will start to store in a way similar to scientific notation in math, which is obviously not as precise as just plain number.

Fortunately, JavaScript gives us a nice constant: Number.MAX_SAFE_INTEGER which is equal to 9007199254740991. As long as you do not go above this value, you will be fine. Also note that a classic integer cannot even be close to that value anyway.

Another thing that people notice that 0.1 + 0.2 is not equal to 0.3, whereas 0.1 + 0.5 is equal to 0.6. But honestly, on this one, you should just check the spec of IEEE 754, which is a standard for floating point numbers used by many languages. You even have articles that enters in the details of that.

JavaScript vs min() and max()

Everybody knows those functions!

Math.min(3, 6, 4) // 3
Math.max(3, 6, 4) // 6

But the problem is not this use case that everybody use, the problem comes with:

Math.min() // Infinity
Math.max() // -Infinity

In fact this one is fairly simple to explain with this simple implementation of max, for example:

function max(...args) {
  let maximum = -Infinity
  for (const n of args) {
    if (n > maximum) {
      maximum = n
    }
  }
  return maximum
}

You can see that, if no args are provided, we never neither the loop nor the condition, which is inside the loop, returning -Infinity, which is the default value. Any other value could cause the function to returns the default value instead of the maximum if said maximum was itself -Infinity.

JavaScript vs coercion round 1 : + operator

Coercion is maybe the most criticized part of JavaScript. But, what is it? Coercion is the fact that, even though JavaScript variables ARE typed (you can even use typeof on JavaScript variables), JavaScript aggressively perform casts.

I perfectly understand that a lot of person do not like this part of the specification. However, you have to understand that coercion is completely specified and is not random.

[] + [] // ""
[] + {} // "[object Object]"
{} + [] // 0
true + true + true // 3
1 + '1' // "11"

When using the + operator, JavaScript has a simple method:

  • Both values are numbers, add them
  • Both values are strings, concatenate them
  • Values are booleans, true = 1, false = 0
  • In any other case, cast them to string, then use the string version.

So, for the first line, we have 2 empty arrays, they are casted to strings. When arrays are casted, all elements in array are casted to strings, and concatened together, using comma as separator. Array being empty, we just concatenate empty strings, which gives un, an empty string.

For the second line, it works exactly the same way, but object, casted to a string, gives us the string [object Object].

For the third line, we should expect the same result, right? Well, the issue is that what you think as an object is not an object, but an empty block. Instead of the binary plus operator, you get the unary plus operator, which cast the array to a number (because that’s what the unary plus operator do). To do so on an array, it casts it to a string then to a number. Casting an empty string to a number gives 0. To get the same result as above, simply put brackets around the curly braces, to force the JavaScript engine to interpret curly braces as object.

The fourth line is straightfoward and inherited from the C: true = 1, false = 0.

The last line is also straightforward: differents value types, both are casted to string and then concatenated.

JavaScript vs coercion round 2 : – operator and equality

true - true === 0 // true
11 - '1' // 10
true == 1 // true
true === 1 // false
[] == 0 // true

Let’s start by the substraction. This time, JavaScript will just cast everything to number to subtract them. Boolean are casted to 1 for true and 0 for false. String are converted to the decimal number they correspond to, NaN otherwise.

Equality is also fairly simple, but with more operators than some languages: === and !==. Those 2 operators just prevent coercion. true === 1 will obviously be false without coercion. With coercion, as we already said, true = 1 and false = 0. Also with coercion, array are casted to string, then to numbers, we get a behavior similar to those with the addition, so not random at all.

JS vs the 6 characters

(!+[]+[]+![]).length // 9

Sometimes, we can makes some funny stuff with coercion. How does this work?
Let’s divided this in 2 parts: !+[]+[] and ![].

In the first part:

  • +[] = 0
  • !0 = true, because not force boolean conversion, 0 = false, and not false is true
  • true + [] = “true”, because two values of different types, both converted to string, check above.

In the second part:

  • ![] = false, because in JavaScript, 0, empty string, false, NaN, null and undefined are falsy values. Everything else is truthy. So, casting an array (truthy) to boolean using the not gives use not true, which is false.

Then, we do “true” + false, which gives us “truefalse” and then we get the length of this string, which is 9.

I do not expect you to like this coercion stuff, but just to admit, it is not random at all. If you want to see more coercion, the website JScrewIt can show how to completely abuse this coercion to write any JavaScript code using only 6 characters.

JavaScript vs map and parseInt

This one has been shown to me recently, but was fairly easy to explain:

['1', '2', '3'].map(parseInt) // [ 1, NaN, NaN ]
['3', '2', '1'].map(parseInt) // [ 3, NaN, 1 ]

So: why some numbers are parsed and some are not? Why the order change something?

In fact, parseInt takes 2 parameters: the numbers to cast and the base to use (10 for decimal, 16 for hexadecimal, 2 for binary, etc…). It is even considered good practice to always specify this parameter.

The map method send up to 3 parameters to the callback function: the element, the index of the element in the array and the complete array.

In the first case, we get:

  • parseInt(‘1’, 0): 0 is ignored, as if no parameters were passed, so it returns 1
  • parseInt(‘2’, 1): the number 2 does not exists in base 1 (only 0), so it returns NaN
  • parseInt(‘3’, 2): same as above

In the second case, we get:

  • parseInt(‘3’, 0): once again, it parses normally, we get 3
  • parseInt(‘2’, 1): the number 2 still not exists in base 1, we get NaN
  • parseInt(‘1’, 2): this time, 1 exists in the binary base, we get 1

So, what to do? Here is the good way to write it:

['3', '2', '1'].map((n) => parseInt(n, 10)) // [ 3, 2, 1 ]

Conclusion

JavaScript is not random. Period. You can critisize JavaScript, this language has a lot of issues, you can dislike the coercion and prefer strongly typed languages. But please, critisize JavaScript about things that are true.