Why does JavaScript have so many ways to represent unknown values? Of the ones I’ve found, only a couple seem necessary. The rest of these unknowns create confusion and lead to defects in code. It’s an unfortunate situation, so one worth understanding if you code in JavaScript.

Below, I summarize how we can represent missing data, unknowns, or undefined values in JavaScript. In this article, I’ll give more details to each of them.

The Simple Cases

null Null is the classic null value. It is generally used to mean that a value is optional, but has not been provided. I assume many coders would be happy if this were the only undefined value in a language. Indeed, many other languages get away with just one. But not JavaScript; we’re just getting started.

undefined as missing The concept of undefined initially seems to make sense. Whereas null specifies an actual value, an undefined would indicate the value doesn’t actually exist. This makes sense if you consider this structure, using TypeScript notation: interface Arguments { nullable_value : number | null optional_value? : number | null } We’re saying that nullable_value is a required property of this structure: you can’t omit it. It can, however, be a null value. optional_value is also nullable, but it doesn’t need to exist at all. undefined lets us distinguish between a specified null value, and the value not existing at all. const a = { nullable_value : null , optional_value : null , } const b = { nullable_value : null , } With these variables, a.optional_value === null . In b however, b.optional_value !== null but b.optional_value === undefined , since optional_value isn’t defined in b at all. On casual use, this feels like a decent way to report, or detect, values that are missing from a structure. Alas, lossy comparison has made this confusing. I used === null to check strictly for the null value. The expression b.optional_value == null is true, since an undefined value loosely compares as equal to null . Since equality is the same with the arguments swapped, that means that b.nullable_value == undefined is also true. Most other languages don’t have this concept of undefined. In the C++ space, undefined isn’t a specific value, but means precisely the value is undefined: it does not have a determinate state and could be anything at all.

undefined as a value The comparison to undefined requires that undefined is a symbol in the language. And for consistency of syntax, it is a real value in the language. This is a choice JavaScript made, likely for implementation simplicity, instead of keeping undefined a special symbol. Here’s where the first bit of oddity shows up, in case undefined itself wasn’t already odd enough for you. What if I specify this constant: const c = { nullable_value : null , optional_value : undefined , } I explicitly said optional_value is undefined . So does this structure actually define the optional_value property? If I serialize to JSON and back, using the standard JSON module, the explicit undefined will be stripped, like we didn’t specify it at all. That’s perhaps good. Additionally, the expression c.optional_value === undefined is true. That is good. It seems like it’s equivalent to not specifying the value at all… …but wait? Object.hasOwn( c, ‘optional_value’ ) is true. So c does have the optional_value property. Indeed, if I emit c on the console, it will print out optional_value: undefined . This is in contrast to the object b I defined before, where Object.hasOwn( b, ‘optional_value’ ) is false.