Why is isNaN(null) == false in JS?
Solution 1:
I believe the code is trying to ask, "is x
numeric?" with the specific case here of x = null
. The function isNaN()
can be used to answer this question, but semantically it's referring specifically to the value NaN
. From Wikipedia for NaN
:
NaN (Not a Number) is a value of the numeric data type representing an undefined or unrepresentable value, especially in floating-point calculations.
In most cases we think the answer to "is null numeric?" should be no. However, isNaN(null) == false
is semantically correct, because null
is not NaN
.
Here's the algorithmic explanation:
The function isNaN(x)
attempts to convert the passed parameter to a number1 (equivalent to Number(x)
) and then tests if the value is NaN
. If the parameter can't be converted to a number, Number(x)
will return NaN
2. Therefore, if the conversion of parameter x
to a number results in NaN
, it returns true; otherwise, it returns false.
So in the specific case x = null
, null
is converted to the number 0, (try evaluating Number(null)
and see that it returns 0,) and isNaN(0)
returns false. A string that is only digits can be converted to a number and isNaN also returns false. A string (e.g. 'abcd'
) that cannot be converted to a number will cause isNaN('abcd')
to return true, specifically because Number('abcd')
returns NaN
.
In addition to these apparent edge cases are the standard numerical reasons for returning NaN like 0/0.
As for the seemingly inconsistent tests for equality shown in the question, the behavior of NaN
is specified such that any comparison x == NaN
is false, regardless of the other operand, including NaN
itself1.
Solution 2:
I just ran into this issue myself.
For me, the best way to use isNaN is like so
isNaN(parseInt(myInt))
taking phyzome's example from above,
var x = [undefined, NaN, 'blah', 0/0, null, 0, '0', 1, 1/0, -1/0, Number(5)]
x.map( function(n){ return isNaN(parseInt(n))})
[true, true, true, true, true, false, false, false, true, true, false]
( I aligned the result according to the input, hope it makes it easier to read. )
This seems better to me.
Solution 3:
(My other comment takes a practical approach. Here's the theoretical side.)
I looked up the ECMA 262 standard, which is what Javascript implements. Their specification for isNan:
Applies ToNumber to its argument, then returns true if the result is NaN, and otherwise returns false.
Section 9.3 specifies the behavior of ToNumber
(which is not a callable function, but rather a component of the type conversion system). To summarize the table, certain input types can produce a NaN. These are type undefined
, type number
(but only the value NaN
), any object whose primitive representation is NaN
, and any string
that cannot be parsed. This leaves undefined
, NaN
, new Number(NaN)
, and most strings.
Any such input that produces NaN
as an output when passed to ToNumber
will produce a true
when fed to isNaN
. Since null
can successfully be converted to a number, it does not produce true
.
And that is why.
Solution 4:
This is indeed disturbing. Here is an array of values that I tested:
var x = [undefined, NaN, 'blah', 0/0, null, 0, '0', 1, 1/0, -1/0, Number(5)]
It evaluates (in the Firebug console) to:
,NaN,blah,NaN,,0,0,1,Infinity,-Infinity,5
When I call x.map(isNaN)
(to call isNaN on each value), I get:
true,true,true,true,false,false,false,false,false,false,false
In conclusion, isNaN
looks pretty useless! (Edit: Except it turns out isNaN is only defined over Number, in which case it works just fine -- just with a misleading name.)
Incidentally, here are the types of those values:
x.map(function(n){return typeof n})
-> undefined,number,string,number,object,number,string,number,number,number,number