NaN stands for "Not a Number." It is the value JavaScript gives you when a calculation has no sensible numeric answer. You met it briefly in JS Numbers; this lesson covers where it comes from, why it spreads through your math, and the one correct way to check for it.
You get NaN whenever JavaScript tries to do math and cannot produce a real number:
console.log(100 / "Apple"); // "Apple" cannot become a number
console.log(100 / "10"); // "10" CAN become a number: no NaN
console.log(0 / 0);
console.log(Math.sqrt(-1));
console.log(parseInt("abc"));
console.log(Number("12px"));
console.log(undefined + 1);
console.log(Infinity - Infinity);
Notice the second line. JavaScript converts a numeric string like "10" to a number before dividing. Only text that cannot be read as a number, like "Apple", produces NaN.
It sounds like a contradiction, but NaN belongs to the number type. It is a special number value that means "this math went wrong":
console.log(typeof NaN);
Any math that touches NaN gives NaN. One bad value can quietly ruin every result that depends on it. A classic source is averaging an empty list, which divides 0 by 0:
const scores = [];
let sum = 0;
for (const s of scores) sum += s;
console.log(sum / scores.length); // 0 / 0
console.log(NaN + 5, Math.round(NaN), Math.max(1, NaN));
console.log("Average: " + sum / scores.length);
NaN, work backward through the calculation and log each input. The bug is almost never in the last step; it is the first value that was already NaN, or text that never became a number.
NaN is the only value in JavaScript that is not equal to itself. Every comparison with it is false:
const x = NaN;
console.log(x == x, x === x);
console.log(NaN > 0, NaN < 0, NaN >= NaN);
console.log(x !== x); // true ONLY when x is NaN
if (result === NaN) is always false, even when result is NaN. You need a function that checks for it.
There are two functions, and they give different answers. The older global isNaN() converts its argument to a number first, then checks. Number.isNaN() (added in 2015) does no converting: it is only true for the actual NaN value.
console.log(isNaN("Apple"), Number.isNaN("Apple"));
console.log(isNaN(undefined), Number.isNaN(undefined));
console.log(isNaN(""), isNaN("12")); // both convert to real numbers
console.log(Number.isNaN(NaN), Number.isNaN(0 / 0));
"Apple" is a string, not NaN. The global isNaN says true only because Number("Apple") would be NaN. That makes it a "would this fail as a number?" test, which is sometimes what you want, but it is easy to misread.
isNaN() converts first, so isNaN("Apple") is true even though "Apple" is not NaN. To test whether a value is NaN, use Number.isNaN().
| Check | x = NaN | x = "Apple" | x = 5 |
|---|---|---|---|
| x !== x | true | false | false |
| isNaN(x) | true | true | false |
| Number.isNaN(x) | true | false | false |
| Number.isFinite(x) | false | false | true |
The last row is often the check you really want: Number.isFinite(x) is true only for a real, usable number (not NaN, not Infinity, and not a string).
Because NaN is not equal to itself, some tools cannot find it and others can:
const list = [1, NaN, 3];
console.log(list.indexOf(NaN), list.includes(NaN));
console.log(new Set([NaN, NaN]).size);
console.log(Object.is(NaN, NaN));
console.log(JSON.stringify({ total: NaN }));
indexOf uses ===, so it never finds NaN. includes uses a smarter comparison that does.NaNs as the same value.Object.is(a, b) is a strict "are these the same value?" check that, unlike ===, says NaN is the same as NaN.NaN: JSON.stringify quietly turns it into null.NaN is one of the 8 falsy values, so || replaces it. But ?? does not, because it only replaces null and undefined:
const price = Number("free");
console.log(price || 0); // NaN is falsy: replaced
console.log(price ?? 0); // ?? ignores NaN
console.log(Number.isNaN(price) ? 0 : price); // the precise version
Text typed into a web page arrives as a string. Convert it, then check before using it:
const inputs = ["3", "three", "", " "];
for (const text of inputs) {
const qty = Number(text);
console.log(JSON.stringify(text), qty, Number.isNaN(qty));
}
Number("") and Number(" ") are 0. If a user leaves a box blank, a NaN check will not catch it. Check for blank text first: if (text.trim() === "").
What does this log?
console.log([NaN].indexOf(NaN), [NaN].includes(NaN));
-1 true. indexOf compares with ===, and NaN === NaN is false, so it never finds it. includes handles NaN correctly.Which of these is true?
NaN === NaNisNaN("hello")Number.isNaN("hello")typeof NaN === "NaN"isNaN converts "hello" to a number first, which gives NaN. C is false because "hello" is a string, not NaN. D is false: typeof NaN is "number".const total = Number(input) ?? 0; still gives NaN when input is "abc". Why, and how do you fix it?
?? only replaces null and undefined, and NaN is neither. Use Number.isNaN(n) ? 0 : n, or || 0 if replacing a real 0 with 0 is fine too.NaN in five different ways and log each oneisNaN and Number.isNaN on "42", "forty-two", and NaNaverage(list) that returns 0 for an empty list instead of NaNreadQuantity(text) that returns a number, or null for blank or non-numeric text