JavaScript has so many strange quirks! Today, while using parseFloat to convert a string into a number, I learned that if the input can’t be converted to a number, it returns NaN (Not a Number). So I wrote the following code to check for it:
var score = parseFloat(prompt("Score:"));
if(score === NaN) {
console.log("Not a Number");
} else {
console.log(score);
}At first glance, this code seems perfectly reasonable, just like comparing undefined or null directly with equality. Who knew it would completely fail?
In JS, it’s interesting: NaN is Number (Not a Number is a Number). If a return value is undefined, you can compare it directly, because the type of undefined is undefined. Intuitively, you’d expect NaN to be the same, with a type of NaN. But if you check, you’ll find that the type of NaN is actually Number. When you run the following comparisons, they all return false!
NaN == NaN "NaN" == NaN "Hi" == NaN
This pitfall is really deep. The solution isn’t hard: use the isNaN function to check, for example:
var score = parseFloat(prompt("Score:"));
if(isNaN(score)) {
console.log("Not a Number");
} else {
console.log(score);
}This seems to solve the problem, but look closely at the following comparisons:
isNaN(NaN) == true
isNaN("hi") == true
isNaN(undefined) == true
isNaN(10) == false
isNaN("10") == falseAlthough isNaN seems to solve the problem, you’ll notice it isn’t a NaN === NaN check. isNaN(“hi”) returns true, but “hi” is a string. It’s fair to say in plain terms that “hi” is not a number, but strictly speaking, “hi” === NaN should be false, because “hi” is a string while NaN is of the number type! This happens because when you pass an argument to isNaN, it is first converted to a Number. In other words, it checks Number(“hi”) === NaN, and Number(“hi”) itself returns NaN.
If you want a strict version of isNaN where only NaN counts as NaN, you can use Number.isNaN. See the following comparisons:
Number.isNaN(NaN) == true
Number.isNaN("hi") == false
Number.isNaN(undefined) == false