Back

The Strange Life of NaN in JavaScript

The Strange Life of NaN in JavaScript

You’ve seen it before: a calculation returns NaN, and suddenly your entire data pipeline produces garbage. Or worse, a comparison that should catch the problem silently fails because NaN === NaN returns false. Understanding JavaScript’s NaN quirks isn’t optional—it’s essential for writing reliable numeric code.

This article explains why NaN behaves the way it does, where it commonly appears, and how to detect it correctly using modern methods.

Key Takeaways

  • NaN is a valid number type per IEEE 754, representing undefined or unrepresentable mathematical results.
  • NaN never equals itself—use Number.isNaN() or Object.is() for reliable detection.
  • The global isNaN() coerces arguments first, causing false positives; avoid it.
  • JSON serialization silently converts NaN to null, while structuredClone() preserves it.
  • Validate numeric inputs at boundaries to prevent NaN from propagating through calculations.

Why NaN Exists and Why typeof Returns “number”

NaN stands for “Not a Number,” yet typeof NaN returns "number". This isn’t a JavaScript bug—it’s by design.

The IEEE 754 floating-point standard, which JavaScript follows, defines NaN as a special value within the number type. It represents the result of undefined or unrepresentable mathematical operations. Think of it as a placeholder that says “this computation didn’t produce a valid numeric result.”

console.log(typeof NaN) // "number"

NaN exists because numeric operations need a way to signal failure without throwing exceptions. Division by zero with integers would crash in many languages, but 0 / 0 returns NaN and lets your program continue.

Common Sources of NaN in JavaScript

NaN appears more often than you might expect:

// Failed parsing
Number("hello")        // NaN
parseInt("abc")        // NaN

// Invalid math operations
0 / 0                  // NaN
Math.sqrt(-1)          // NaN
Infinity - Infinity    // NaN

// Operations with undefined
undefined * 5          // NaN

Form inputs are a frequent culprit. When parseFloat(userInput) encounters non-numeric text, NaN silently enters your calculations and propagates through every subsequent operation.

NaN Comparison Rules: Why NaN !== NaN

Here’s the behavior that trips up most developers:

NaN === NaN  // false
NaN !== NaN  // true

IEEE 754 mandates this. The reasoning: if two operations both fail, you can’t assume their “failures” are equivalent. Math.sqrt(-1) and 0 / 0 both produce NaN, but they represent different undefined results.

This means you cannot use equality operators to detect NaN.

Number.isNaN vs isNaN: The Critical Difference

JavaScript provides two functions for NaN detection, but only one works reliably.

The global isNaN() coerces its argument to a number first:

isNaN("hello")     // true (coerces to NaN, then checks)
isNaN(undefined)   // true
isNaN({})          // true

This produces false positives. The string "hello" isn’t NaN—it’s a string that becomes NaN when coerced.

Number.isNaN() checks without coercion:

Number.isNaN("hello")     // false
Number.isNaN(undefined)   // false
Number.isNaN(NaN)         // true

Always use Number.isNaN() for accurate detection. Alternatively, Object.is(value, NaN) also works correctly:

Object.is(NaN, NaN)  // true

JSON NaN Behavior: Silent Data Loss

When you serialize data containing NaN, JSON.stringify() replaces it with null:

JSON.stringify({ value: NaN })  // '{"value":null}'
JSON.stringify([1, NaN, 3])     // '[1,null,3]'

This is a common source of bugs when sending numeric data to APIs or storing it in databases. Your NaN values disappear silently, and you receive null back—which may cause different errors downstream.

Validate numeric data before serialization if NaN preservation matters.

structuredClone NaN: Preservation in Modern Cloning

Unlike JSON, structuredClone() preserves NaN values:

const original = { score: NaN }
const cloned = structuredClone(original)

Number.isNaN(cloned.score)  // true

This makes structuredClone() NaN-safe for deep copying objects that may contain invalid numeric results. If you’re cloning data structures with potential NaN values, prefer structuredClone() over JSON round-tripping.

NaN Propagation: The Viral Effect

Once NaN enters a calculation, it infects every result:

const result = 5 + NaN      // NaN
const final = result * 100  // NaN

This propagation is intentional—it prevents you from accidentally using corrupted data. But it means a single NaN early in a pipeline can invalidate an entire dataset.

Validate inputs at boundaries: when parsing user input, receiving API responses, or reading from external sources.

Conclusion

NaN’s strange behavior follows logical rules once you understand IEEE 754’s design. Use Number.isNaN() or Object.is() for detection—never equality operators or the global isNaN(). Remember that JSON serialization converts NaN to null, while structuredClone() preserves it. Validate numeric data at entry points to catch NaN before it propagates through your calculations.

FAQs

NaN is defined as a special numeric value by the IEEE 754 floating-point standard, which JavaScript implements. It represents the result of undefined mathematical operations while remaining within the number type system. This design allows numeric computations to continue without throwing exceptions when operations fail.

No. NaN is the only JavaScript value that is not equal to itself. Both NaN === NaN and NaN == NaN return false. Use Number.isNaN() or Object.is(value, NaN) instead for reliable detection.

The global isNaN() coerces its argument to a number before checking, causing false positives for non-numeric values like strings or undefined. Number.isNaN() performs no coercion and returns true only if the value is exactly NaN. Always prefer Number.isNaN() for accurate results.

Validate all numeric inputs at entry points such as form fields, API responses, and external data sources. Check values with Number.isNaN() immediately after parsing or receiving data. This stops NaN from propagating through subsequent operations and invalidating your results.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay