Demystifying isNaN and Number.isNaN in JavaScript
NaN (“Not a Number”) in Javascript looks like an error value, but it’s actually a number. It compares unequal to itself. It has two competing functions to detect it: the global isNaN() and the newer Number.isNaN().
As developers, we can’t treat this as trivia. Data validation, numeric pipelines, and type safety all depend on knowing exactly which one to use.
1. A Little History
When JavaScript was created in 1995, the global function isNaN() was added. The idea was simple: check if something is NaN. But there was a twist: it first coerces the input into a number.
isNaN("123"); // false - coerces "123" into 123
isNaN("abc"); // true - coerces "abc" into NaN
isNaN(undefined);// true - coerces undefined into NaN
isNaN(""); // false - coerces "" into 0Coercion
That coercion made sense for form inputs in the early web. But in modern applications, it’s dangerous: empty strings and undefined suddenly look like “valid” numbers. ECMAScript 2015 (ES6) introduced Number.isNaN() as the strict, no-coercion alternative.
2. What Exactly Does Number.isNaN Do?
Number.isNaN(value) asks one—and only one—question: 👉 “Is this value literally the special NaN number?”
That means:
Number.isNaN(NaN); // true
Number.isNaN(56); // false → valid number
Number.isNaN("56"); // false → string, not NaN
Number.isNaN("abc"); // false → string, not NaN
Number.isNaN(undefined); // false → not NaNNote how;
- You know the data you are dealing with like With isNaN("abc"), you’d get true (because coercion turns "abc" to NaN), which can mislead you into thinking "abc" is “already” NaN.
- It separates type-checking from numeric validation
- Number.isNaN answers: “Is this value literally NaN?”
- Parsing (Number() / parseInt) answers: “Can this value become a number?”
- It avoids false positives with isNaN("") // false, you get 0 — misleading, with isNaN(undefined) // true, you get NaN — misleading.
3. But wait... NaN === NaN
NaN === NaN; // falseNaN is the only value in JavaScript that is not equal to itself.NaN represents “an unrepresentable result,” and by definition, such results can never be equal.
This means:
- 0/0 - NaN
- Math.sqrt(-1) - NaN
- parseInt("xyz") - NaN
comparing them
0/0 === Math.sqrt(-1); // false4. Example: Input Validation
If we had a checkout form:
const form = {
price: "199",
discount: "abc", // user error
tax: "" // left blank
};if we used isNaN
isNaN(form.discount); // true → coerced into NaN
isNaN(form.tax); // false → coerced "" into 0That empty tax field passes validation and silently becomes 0. Dangerous.
with Number.isNaN
Number.isNaN(form.discount); // false → it's a string, not NaN
Number.isNaN(form.tax); // false → also a string, not NaNBoth are safe but aren't they invalid? That is why we need parsing.
5. Parsing + Number.isNaN:
The modern way would be:
- Parse user input with Number() (or parseFloat/parseInt if appropriate).
- Use Number.isNaN to check if the result is valid.
const safeNumber = input => {
const n = Number(input);
return Number.isNaN(n) ? null : n;
};
safeNumber("199"); // 199
safeNumber("abc"); // null
safeNumber(""); // nullnow our form is safely validated
const processed = {
price: safeNumber(form.price), // 199
discount: safeNumber(form.discount), // null
tax: safeNumber(form.tax) // null
};7. Why this matters in production
- Silent bugs: Empty strings ("") become 0 under isNaN.
- Loose assumptions: undefined becomes NaN when coerced.
- Math safety: Once NaN gets into your pipeline, every operation downstream becomes NaN.
const subtotal = Number("199");
const discount = Number("abc"); // NaN
console.log(subtotal - discount); // NaN8. Alternatives ways to look at it
Since NaN is the only value unequal to itself: we can do
const isReallyNaN = x => x !== x;
isReallyNaN(NaN); // trueOr use ES6’s Object.is, which handles it more elegantly:
Object.is(NaN, NaN); // truejavascript book
If this interested you, check out my Javascript Book