Not-A-Number: when javascript gets crazy

Yesterday I was doing some maintenance on a corporate website when I found out that parseInt() returns NaN if the argument is, well, not a number.

> let pippo = parseInt('a')
NaN

NaN is a constant and stands for Not a Number. Javascript uses this for some very default cases, for example if you divide a number by a string, etc.

That’s fair. What comes next is funny :)

> pippo == NaN
false
> pippo === NaN
false

Even if a variable is NaN, you cannot check with the usual operators that it is NaN.

> pippo == pippo
false
> pippo === pippo
false
> NaN == NaN
false
> NaN === NaN
false

Things start to get crazy. As you can see, NaN is not even equal to itself. You cannot compare anything to NaN, because NaN is not equal to anything.

sooooo… isNaN() ?

There is a function called isNaN() and it’s the only way to detect a NaN.

> isNaN(pippo)
true
> isNaN(3)
false
> isNaN('bau bau micio micio')
true

This function seems to resolve our problems: it detects not-numbers, and infact a string is considered NaN.

> isNaN(undefined)
true
> isNaN(null)
false

I see your face 😆 Where is the logic?

isNaN tries to convert the argument to a number, using the constructor Number(); if the argument cannot be converted, NaN is returned.

In the case of isNaN(null), this is what happens:

isNaN(null) // Number(null) ==> 0 
> false 

Ha ha, Number(null) returns zero??? Welcome to javascript 😎 This would require a bigger explanation, however JS does a type cohercion trying to compare things. It is described here (warning: it’s a spec, the first 10 lines are understandable, then it’s a mess).

Of course,

null == 0
> false

🙂

Just to add the last bit of discoveries around NaN:

Number(null)
> 0
parseInt(null) 
> NaN

There is another isNaN() around

The Javascript Number object also has a isNaN method, that is way more conservative:

> Number.isNaN(3)
false
> Number.isNaN('3a')
false
> Number.isNaN('abc')
false
> Number.isNaN(undefined)
false
> Number.isNaN(null)
false
> Number.isNaN(NaN)
true

It will return true only for NaN.

Stop this! It’s a mess!

Ok, let’s end with a song:

console.log(`${10/'a'}a${ 10/'b'}a${ 10/'c'}a${10/'d'}a ${10/'e'}a${10/'f'}a${10/'g'}a${ 10/'h'}a Batman!`)
//NaNaNaNaNaNaNaNa NaNaNaNaNaNaNaNa Batman!