Null, Undefined, and NaN: What Each Really Means
Three values that all mean 'nothing or invalid' in JavaScript — but null, undefined, and NaN each tell a different story. Here is how to read them.
JavaScript gives you three different ways to say “there is nothing useful here,” and they are not interchangeable. null, undefined, and NaN each describe a distinct kind of absence or invalidity, and reading them correctly tells you what actually went wrong.
null vs undefined: who decided there is nothing
The cleanest way to keep these straight is to ask who set the value.
undefined is what the language gives you when nobody assigned anything. A variable you declared but never set is undefined. A property that does not exist on an object is undefined. A function that runs off the end without a return statement evaluates to undefined. An argument you forgot to pass is undefined. In every case, the absence happened automatically — it is the default state of “not filled in.”
let x; // undefined — declared, never assignedconst o = {};o.missing; // undefined — no such propertyfunction f() {}f(); // undefined — no return valuenull, by contrast, is something you write on purpose. It is an explicit, deliberate “no value here.” When you set user.avatar = null, you are stating that the absence is intentional and meaningful — there genuinely is no avatar, as opposed to “we never looked.” This distinction is the whole point of having both: undefined tends to mean “uninitialized or unknown,” while null tends to mean “known to be empty.”
A famous wart reinforces how separate they are at the type level:
typeof undefined; // "undefined"typeof null; // "object" ← historical bug, never fixedtypeof null returning "object" is widely regarded as a bug in the original implementation. It was never corrected because too much existing code depends on the current behavior, so it has become a permanent quirk. To reliably check for null, compare directly: value === null.
One more practical note: loose equality blurs the two. null == undefined is true, but null === undefined is false. Most modern code uses strict equality (===) precisely to keep them distinguishable.
NaN: the error value that lives inside numbers
NaN stands for “Not a Number,” which is misleading — typeof NaN is actually "number". It is a special value defined by the IEEE 754 floating-point standard that every modern language uses for floating-point math. NaN represents the result of a numeric operation that has no meaningful numeric answer.
You get it from operations like 0 / 0, Math.sqrt(-1), Infinity - Infinity, or trying to coerce something non-numeric: Number("hello") and parseInt("abc") both produce NaN. The idea is that once a calculation goes invalid, the error propagates instead of throwing — any arithmetic involving NaN produces NaN again, so a single bad value can quietly poison a whole chain of math.
The defining property is strange but important: NaN is the only value in the language not equal to itself.
NaN === NaN; // falseNaN == NaN; // falseThis is required by the floating-point standard — two computations that both failed are not “the same failure.” The practical consequence is that you cannot test for NaN with == or ===. Use Number.isNaN(value), which returns true only for an actual NaN.
How other languages handle the same problem
The “nothing” problem is universal, and the design choices vary. Python has a single None. Ruby and Lua use nil. Go has a typed nil for pointers, maps, slices, and interfaces. Java and C# have null, and dereferencing it throws the infamous NullPointerException — a class of bug so common its inventor, Tony Hoare, later called null his “billion-dollar mistake.”
A different philosophy avoids the magic value entirely. Languages like Rust (Option<T>), Haskell (Maybe), and Swift (optionals) make absence part of the type system: a value is either Some(x) / Just x or explicitly None / Nothing, and the compiler forces you to handle the empty case before you can use the value. That turns “I forgot it might be missing” from a runtime crash into a compile error.
For invalid numeric results, the IEEE 754 NaN is nearly universal across languages that use floating point, so the “not equal to itself” rule you learned in JavaScript holds in Python, C, Java, and most others too.
FAQ
FAQ
Should my functions return null or undefined for a missing value?+
Why does typeof NaN say 'number'?+
What is the difference between == null and === null?+
Related reading
2026-06-04
ACID vs BASE: What Database Guarantees Actually Promise
ACID and BASE describe two ends of a tradeoff between strict correctness and scalable availability. Learn what each guarantee means, when each fits, and why most modern databases sit somewhere in between.
2026-06-04
Big-Endian vs Little-Endian
Byte order explained: how big-endian and little-endian lay out multi-byte numbers in memory, why network protocols pick one, and when the difference actually bites you.
2026-06-04
Big-O Notation in Plain English
Big-O describes how an algorithm's runtime or memory grows as input grows. Learn the common classes — O(1), O(log n), O(n), O(n log n), O(n^2), O(2^n) — with plain examples.
2026-06-04
CORS in Plain English: Why the Browser Blocks Your Fetch
A clear walkthrough of CORS and the same-origin policy — what an origin is, why your fetch fails, how servers opt in, and the big misconception about who CORS actually protects.
2026-06-04
Environment Variables and PATH, Explained
What environment variables actually are, why they hold config and secrets, and how PATH decides which binary runs when you type a command.
Get the best tools, weekly
One email every Friday. No spam, unsubscribe anytime.