NaN, not a number, is a special type value used to denote an unrepresentable value. With JavaScript, NaN
can cause some confusion, starting from its typeof
and all to the way the comparison is handled.
Several operations can lead to NaN
as the result. Here are some examples (follow along on JSBin: jsbin.com/yulef):
Math.sqrt(-2)
Math.log(-1)
0/0
parseFloat('foo')
The first trap for many JavaScript beginners is usually the unexpected result of calling typeof
:
console.log(typeof NaN); // 'number'
In a way, while NaN isn’t supposed to be a number, its type is number. Got it?
Stay calm, as this will continue to lead to many confusing paths. Let’s compare two NaNs:
var x = Math.sqrt(-2);
var y = Math.log(-1);
console.log(x == y); // false
Maybe that’s because we’re supposed to use strict equal (===) operator instead? Apparently not.
var x = Math.sqrt(-2);
var y = Math.log(-1);
console.log(x === y); // false
Arrgh! Could it be because they are NaNs from two different operations? What about…
var x = Math.sqrt(-2);
var y = Math.sqrt(-2);
console.log(x == y); // false
Even crazier:
var x = Math.sqrt(-2);
console.log(x == x); // false
What about comparing two real NaNs?
console.log(NaN === NaN); // false
Because there are many ways to represent a NaN, it makes sense that one NaN will not be equal to another NaN. Still, this is the reason why I sometimes tweet:
This is your annual reminder that NaN stands for "Not a NaN".
— Ariya Hidayat (@AriyaHidayat) October 23, 2013
To solve this, originally I intended to submit this proposal for ECMAScript 7:
But of course, solutions (and workarounds) already exist today.
Let’s get to know the global function isNaN:
console.log(isNaN(NaN)); // true
Alas, isNan()
has its own well-known flaws:
console.log(isNaN('hello')); // true
console.log(isNaN(['x'])); // true
console.log(isNaN({})); // true
This often leads to a number of different workarounds. One example is to exploit the non-reflective nature of NaN (see e.g. Kit Cambridge’s note):
var My = {
isNaN: function (x) { return x !== x; }
}
Another example is to check for the value’s type first (to prevent coercion):
My.isNaN = function(x) { return typeof x === 'number' && isNaN(x); };
Note: The coercion that is being blocked here is related to isNaN
. As an exercise, compare the result of isNaN(2)
, isNaN('2')
and isNaN('two')
.
Fortunately, for the upcoming ECMAScript 6, there is Number.isNaN()
which provides a true NaN
detection (BTW, you can already use this function in the latest version of Chrome and Firefox). In the latest draft from April 2014 (Rev 24), this is specified in Section 20.1.2.4:
When the Number.isNaN is called with one argument number, the following steps are taken:
If Type(number) is not Number, return false.
If number is NaN, return true.
Otherwise, return false.
In other words, it returns true
only if the argument is really NaN:
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(Math.sqrt(-2))); // true
console.log(Number.isNaN('hello')); // false
console.log(Number.isNaN(['x'])); // false
console.log(Number.isNaN({})); // false
Next time you need to deal with NaN, be extremely careful!