pickuma.
Dev Knowledge

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.

5 min read

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 assigned
const o = {};
o.missing; // undefined — no such property
function f() {}
f(); // undefined — no return value

null, 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 fixed

typeof 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; // false
NaN == NaN; // false

This 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?+
Pick one convention and stay consistent. A common pattern is to use undefined for 'this was never set or found' and null for 'intentionally and explicitly empty.' Many codebases and APIs prefer null for absent return values because it reads as a deliberate signal rather than an accident.
Why does typeof NaN say 'number'?+
Because NaN is a value within the floating-point number type, not a separate type. It marks a number-producing operation that failed, so it stays in the numeric domain. The name 'Not a Number' describes its meaning, not its type.
What is the difference between == null and === null?+
value === null is true only for actual null. value == null is true for both null and undefined, because loose equality treats them as equal. The == null check is a deliberate shorthand some developers use to catch both 'nothing' values at once.

Related reading

See all Dev Knowledge articles →

Get the best tools, weekly

One email every Friday. No spam, unsubscribe anytime.