At the end of the day it would have been much nicer for there to only have been one nothing type but what’s done is done.
Element.{firstChild, nextSibling} being null means “I know what firstChild (or nextSibling) is, but this element doesn’t have one.”
It would be categorically wrong for things like Element.{firstChild, nextSibling} to be undefined.
I’ve seen null/undefined semantics play out; they more or less collapse under the slightest of deadline pressures or scaling of team sizes. There are infinite ways to splice the idea of “emptiness” or “lack of data” and any definition you provide (the definitions both you and OP provide give a lot of wiggle room) would be interpreted fifteen ways by fifteen developers, so much so that even changing hands just once will cause any null/undefined distinctions to sublimate in any codebase.
There are so many languages which do just fine with one null type; I don’t understand why JavaScript developers have to do these sorts of mental kinesthetics to justify the existence of two in JavaScript, especially given the more or less insane fact that `typeof null === "object"`. If you consistently use undefined or null exclusively internally, and accept both via the == null trick externally, you can pretty much avoid having to deal with null/undefined semantics entirely.
> There are so many languages which do just fine with one null type; […] two in JavaScript
I quibble over your claim here, because JavaScript doesn’t have two null types. undefined is not a null type. Rather, it’s JavaScript’s equivalent of what is in most dynamically-typed languages an exception, and a compilation error in most or all statically-typed languages. As a concrete example, where JavaScript produces undefined, Python raises AttributeError or KeyError. There are similarities to NaN as well—both come from a philosophy of trying to keep the code running even if you ask for something that isn’t defined. Yes, undefined then behaves a lot like null in some ways, just as NaN behaves like a number in some ways (though the two cases are not parallel, merely passingly similar). But then, false behaves like null in most of the same ways too (though not quite all—most notably, the `== null` comparison—but it’s well understood that a lot of JavaScript’s == comparisons are illogical and inconsistent, so this should not be considered significant to the design); yet no one claims false to be a third null value.
(This is a fundamental problem of nullable types which is solved by algebraic data types; in Rust, the equivalent method would return None for “no value found”, or Some(None) if it found a None value.)
var x = {}.foo;
var y = (function() {})();
These are contrived examples, but they represent things done by very reasonable code. There are all kinds of ways that `undefined` can get assigned into a variable. It's inevitable that those cases would have to be handled.Yes, you have to handle cases where things are undefined, but I'd be wary of handling them too dissimilarly to where things are null, because `undefined` was not designed to be "Null 2: Electric Boogaloo", it was designed to be "404 Item Not Found Exception" in the time before JS had real exceptions.