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!