What is nan in JavaScript?
NaN is a special numeric value in JavaScript that signals an invalid math result, like dividing zero by zero or parsing a non-numeric string.
Theory
TL;DR
- NaN stands for "Not-a-Number," yet
typeof NaN === "number"— it lives inside the number type - Think of it as a poisoned pill: one NaN in a calculation contaminates every result that follows
NaN !== NaNis alwaysfalse. Not a bug, that is the specNumber.isNaN()is the correct check. GlobalisNaN()coerces strings first and gives wrong answers
Quick example
console.log(0 / 0); // NaN
console.log(typeof NaN); // "number"
console.log(NaN === NaN); // false
console.log(Number.isNaN(NaN)); // true
const result = "abc" * 5;
console.log(result + 10); // NaN (propagates)NaN spreads. Any arithmetic on NaN gives back NaN. That one bad input can ruin a whole chain of calculations with no error thrown.
Why NaN !== NaN
The IEEE 754 floating-point standard defines NaN as "unordered." Two invalid results can come from completely different broken operations: 0/0 and Math.sqrt(-1) are both NaN, but they describe different kinds of invalid math. So the standard says equality with NaN always returns false. This forces you to detect it explicitly rather than accidentally trusting it.
V8 stores NaN as the bit pattern 0x7FF8000000000000. The === operator compares bit patterns, and any comparison involving NaN returns false by spec. No exceptions.
When to use Number.isNaN()
- Input parsing: check
Number.isNaN()before any calculation on user input - API responses: replace NaN with
nullbefore sending JSON (JSON.stringifyconverts NaN tonullanyway, which can break client math without throwing) - Array reductions: filter out NaN before calling
.reduce() - Anywhere you use global
isNaN(): replace it withNumber.isNaN()
How JavaScript creates NaN
Invalid numeric operations produce NaN: dividing zero by zero, taking the square root of a negative number, parsing a non-numeric string with Number() or parseInt(). After that, any arithmetic involving NaN automatically produces NaN. The infection is automatic.
Common mistakes
Comparing with ===
if (result === NaN) { /* this never runs */ }NaN never equals anything, including itself. Fix: Number.isNaN(result).
Using global isNaN() on strings
isNaN("hello"); // true (coerced to NaN)
isNaN("123abc"); // true (coerced to NaN)
isNaN("123"); // false (coerced to 123)Global isNaN() converts the argument first. It cannot distinguish "this is actually NaN" from "this string does not parse as a number." Use Number.isNaN(Number(value)) when you need both checks.
NaN in JSON
JSON.stringify({ score: NaN }); // {"score":null}JSON has no NaN literal. Serialization converts it to null. If your client reads that null and tries to do math, results will be wrong with no error. Sanitize before stringify.
NaN poisoning Math.min / Math.max
Math.min(1, NaN, 3); // NaNOne NaN poisons the whole aggregate. Filter the array first.
Real-world usage
- React / Formik:
Number.isNaN()in numeric field validators before form submission - Express: parse
req.query.agewithparseInt(), check for NaN before database queries - D3.js: filter NaN from chart data arrays before computing scales
- Lodash:
_.isNaN()as a convenience wrapper in utility functions
Follow-up questions
Q: Why does typeof NaN === "number" if NaN is not a valid number?
A: JavaScript's type system follows IEEE 754. NaN is part of the floating-point number space, just an invalid state within it. The type label is technically correct.
Q: What is the difference between isNaN() and Number.isNaN()?
A: Global isNaN() coerces the argument, so isNaN("foo") returns true. Number.isNaN() skips coercion and only returns true for the actual NaN value, so Number.isNaN("foo") returns false.
Q: How do you handle NaN in an array reduce?
A: Filter before reducing: .filter(v => !Number.isNaN(v)). Or guard inside the callback with a fallback value.
Q: What does JSON.stringify do with NaN?
A: It converts NaN to null. JSON has no NaN representation. Always sanitize data before serialization.
Q: (Senior) V8 stores NaN as 0x7FF8000000000000. Why does +NaN === NaN still return false?
A: The unary + returns the same bit pattern. But IEEE 754 specifies that any comparison involving NaN must return false regardless. This is intentional so invalid values cannot accidentally pass equality checks.
Examples
Parsing user input
const userAge = "25a";
const age = parseInt(userAge, 10); // NaN
if (Number.isNaN(age)) {
console.log("Invalid age"); // runs
} else {
console.log(`Age in 5 years: ${age + 5}`);
}
// Output: "Invalid age"parseInt stops at the first non-numeric character and returns NaN. Check before you calculate, not after.
Fixing NaN in array reduce
const prices = ["$10", "20", "abc"];
// Without fix: NaN poisons the sum
const broken = prices.reduce((sum, p) => sum + Number(p), 0);
console.log(broken); // NaN
// With fix: filter invalid values first
const total = prices
.map(p => Number(p.replace("$", "")))
.filter(v => !Number.isNaN(v))
.reduce((sum, v) => sum + v, 0);
console.log(total); // 30I have seen this exact pattern break production dashboards where one bad API value turned an entire summary widget to NaN. The fix is one line.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.