DEV Community

loading...

Understanding weird parts of Javascript

royal_bhati profile image Royal Bhati Updated on ・7 min read

Since JavaScript is a weakly-typed language, values can also convert automatically between different types, and it is called implicit type coercion. People who don't understand the quirks of javascript tend to make fun of the language by sharing memes like this.

js meme

Now in this post, I am going to walk through every example given in this meme and try to explain briefly and try to link references if possible.

While every single example mentioned in this meme would have been a standalone long post, I kept it short to keep it concise and beginner-friendly. Might throw some more light on examples separately in future.

Let's get started

1.

console.log(typeof NaN) // "number";
Enter fullscreen mode Exit fullscreen mode

According to the ECMAScript standard, Numbers should be IEEE-754 floating-point data. This includes Infinity, -Infinity, and also NaN.

NaN stands for Not a number.

Let's see when NaN is returned :

  • Division of zero by zero. ( 0 / 0)
  • Dividing an infinity by infinity. (Inf / Inf)
  • Multiplication of an infinity by a zero. ( Inf * 0)
  • Any operation in which NaN is an operand. (NaN + 2)
  • Converting any undefined or non-numeric string to a number. Number("abc")

Saw anything common?
NaN is returned only when there is a numerical operation.

By definition, NaN is the return value from operations that have an undefined "numerical" result.
So, It is evident that the type of NaN would be a number.

Reference :

2 .

console.log(999999999999) // 10000000000000;
Enter fullscreen mode Exit fullscreen mode

This happens because JavaScript only supports 53-bit integers.
All numbers in JavaScript are floating-point which means that integers are always represented as

sign × mantissa × 2^exponent
Enter fullscreen mode Exit fullscreen mode

The fraction occupies bits 0 to 51, the exponent occupies bits 52 to 62, the sign occupies bit 63.
So js suffers from a loss of precision where the least significant digits disappear if the number is huge.

There has been a recent addition to javascript "BigInt" which solves the problem of representing whole numbers larger than
2^53 - 1

References:

3.

console.log(0.5 + 0.1 == 0.6); // true
console.log(0.1 + 0.2 == 0.3); //false
Enter fullscreen mode Exit fullscreen mode

I have already mentioned it above but gonna repeat it, JavaScript uses a 64-bit floating-point representation according to IEEE 754.
64-bit Binary floating-point format cannot accurately represent a number like 0.1, 0.2 or 0.3 at all. While most of the languages round off the number to give results as expected but JS don't.

If you convert 0.1 to binary representation you would end up with a 0.00011001100110011.... (endless repetition of 0011 ).
This post explains it in detail
In double-precision floating-point (JS), 53 bits are used, so the otherwise infinite representation is rounded to 53 significant bits. So results are always inaccurate in decimal.
This stack overflow answer also explains it very well - https://stackoverflow.com/a/28679423

References :

4 .

console.log(Math.max())  //-Infinity
conosle.log(Math.min()) //+Infinity

Enter fullscreen mode Exit fullscreen mode

First Let's make it clear that...
THEY DON'T RETURN LARGEST OR MINIMUM NUMBERS, for those needs we have Number.MAX_VALUE and NUMBER.MIN_VALUE.

Math. max() and Math. min() are static methods that return maximum and min values among the respective arguments.
So according to the spec, if you call them without any arguments, they would return -Inf and +Inf.

While the spec doesn't say anything regarding why it does that, so I looked into the chromium source code to find that out.
To be honest, I found what I was thinking i.e. whenever you call the Math. max() method with a single argument(Math. max(100)), it compares it with -Infinity and returns the Number itself because if it is a valid number, it would always be more significant than -Infinity.
Same goes for Math.min().
So when there is no argument to compare it returns -Infinity as its the max value between nothing and -Inf.

5.

console.log([]+[]) // ""

Enter fullscreen mode Exit fullscreen mode

According to the spec, when javascript encounters The Addition Operator ( + ) it performs following steps.

These steps will build foundation for upcoming few examples.

a. Convert both operands to Primitive values
b. If any of the operand is type String then return result as string concatenation
c. Else convert both the Operands to Number using ToNumber()
d. If type of one operand is different from other then throw a TypeError
e Else return the mathematical sum

So lets walkthrough our example :

a. First operands are first converted to their primitive values if they are not, which in this case are not primitive .

b. Now the ToPrimitive converts the object type to a primitive type. Along with the input, ToPrimitive also accepts an optional "Preferred type" parameter which is provided to give ToPrimitive a hint of the type.

c. After converting to the primitives if any of the primitive is of type string,then string concatenation takes place which in this case is true (explained below) and we see a "" as a result.

Let's se how ToPrimitive works :

  • When there is no hint is given, ToPrimitive defaults the hint to Number.

  • After deciding the hint, it checks it against list of two methods in a defined order.
    [valueOf, toString] in the case of hint Number and reverse in the case of String.

  • In this case it uses the default hint so the following steps are taken
    a) [].valueof returns Array itself ans since its not primitive, so it goes to the second method
    b) [].toString returns "" and since it returns a primitive value it is returned as a primitive value.

References :
https://tc39.es/ecma262/#sec-addition-operator-plus

6 .

console.log([]+{}) // "[object Object]"

Enter fullscreen mode Exit fullscreen mode

Adding to the explaination above, {}.toString is [object Object] so by string concatenation we get this result.

7.

{} + []
Enter fullscreen mode Exit fullscreen mode

Now this example would return the same result as [] +{}.

But does that mean the meme has a typo?

NO, but If you try that example in the console of chrome or firefox it would return 0.
This is becaus Object literal in starting is treated as an empty code block and is ignored by interpreter and we are left with this
expression " + [] ".
Now the unary "+" operator converts its operand to Number and Number([]) equals to zero.

Reference:
-https://tc39.es/ecma262/#sec-unary-plus-operator

8.


console.log(true + true + true) // 3
console.log( true - true) //0

Enter fullscreen mode Exit fullscreen mode

Now according to algorithm in the 5th point, we already have a primitive value i.e boolean and since none of the operand is a string we convert the first two operands to Numeric and according to the spec, ToNumber(Boolean) is 1 for true and 0 for false.
So true + true gives 1+1 = 2

No we have "2 + true" which is processed same as we did for the first two operands and we get 3.

So both of the results makes sense now.

Reference :
- https://tc39.es/ecma262/#sec-tonumber

9 .

console.log(true ==1) //true
console.log(true ===1) //false
Enter fullscreen mode Exit fullscreen mode
  • First expression is compared using Abstract Equality Comparison which allows coercion so according to the spec

If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.

So using that we get ToNumber(true) ==1

  • Second expression is compared using Strict Equality Comparison which does not allow coercion so according to the spec

If Type(x) is different from Type(y), return false.

Clearly, types are different so result is false

10.

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

Enter fullscreen mode Exit fullscreen mode

This is was a mind fuck when I was first saw this :p

Lets divide this in four pieces.

lets begin with !+[]

Now here, we don't have two operands but two unary operators (! & +) and since ! and + has same precedence we start from left to right.
We first encounter "!" whose associativity is right to left so we evaluate "+[]" which results in 0 (we have already discussed why the result is zero in 7th post)

Now negation operator according to the spec converts the operand into boolean if its not already so if we convert 0 to boolean we get a false.
So, "!false" returns true which is of type boolean.

Now we have (true + [] + ![]).length

Taking "true + []" for evaluation according to the rules of addition operator we get a result "true" of type string because the primitive value of empty array is an empty string and if any of the operand is of type string we perform string concatenation.

Now we are left with ("true"+![]).length

So according to the rules of "!" operator we convert [] to boolean which results in true (According to the spec ToBoolean(object) returns true).
So now,placing true in place of empty array we get "!true" which results in false and since one of our operand is of type string we concatenate the operands which results in "truefalse"

Now its obvious why console.log("truefalse".length) returns 9

Reference :
- https://tc39.es/ecma262/#sec-toboolean
- https://tc39.es/ecma262/#sec-logical-not-operator

11.

console.log(9+"1") //91

Enter fullscreen mode Exit fullscreen mode

We have already discussed this but I am gonna mention it again.
According to js spec if any one of the operand's primitive value is of type string then we concatenate the operands's primitive value which results in "91"

console.log(9-"1") // 90
Enter fullscreen mode Exit fullscreen mode

According to the spec, In subtraction operands are coerced to Number and if the ToNumber results are valid then final result is a mathematical subtraction.

console.log([]==0) //true
Enter fullscreen mode Exit fullscreen mode

As mentioned earlier, Double equals uses Abstract Equality Comparison which allows coercion so our empty array is coneverted to its primitive value which is "" and according to the spec

If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.

and ToNumber("") is 0 so that is why we get a true.


So next time if anyone shares this meme with you then you have answers to keep them shut.

Discussion (4)

pic
Editor guide
Collapse
fly profile image
joon

In terms of usefulness, I'd give this post low points, but this was quite informative and gives enlightenment to all those memes where you end off shrugging 'yeah js is weird I know'.
Thank you for the post, I really enjoyed it :)

Collapse
royal_bhati profile image
Collapse
niketazad profile image
NIKET KUMAR

It is really a magic for me. Thanks for the post!!

Collapse
royal_bhati profile image
Royal Bhati Author

You're welcome :)