JavaScript WTF #4: undefined vs null

The forth place in the JavaScript WTF competition goes to the amazingly large number of ways to represent lack of value. Most C-like languages have keyword null that means “no value”. Beside null, JavaScript also has undefined, apparently meaning “like, totally no value”. The actual situation is even more complicated. A variable in JavaScript can be in any of the following states of no-value-ness:

  1. Not declared.
  2. Not initialized or explicitly set to undefined.
  3. Explicitly set to null.
  4. Temporarily dead (this is not a joke).

There is an impressive number of subtle differences between these states.

Not declared

In default, non-strict mode, not declared variables are treated as not initialized, and have implicit value of undefined. In strict mode, accessing a not declared variable causes a ReferenceError, except when used with typeof.

'use strict';
console.log(typeof notDeclared==="undefined"); // prints true
console.log(notDeclared===undefined);          // ReferenceError

Note that typeof notDeclared returns string "undefined", which is different from undefined value.

Not initialized

Declared, but not initialized variables are implicitly assigned a value of undefined. There is no way to distinguish between a variable that was never initialized, and a variable that was explicitly set to undefined.

var notInitialized;
console.log(typeof notInitialized==="undefined"); // true
console.log(notInitialized===undefined);          // true

Null

Null is interpreted as pointing to no object. It is different from undefined in a number of ways:

var nullVar = null;

console.log(typeof nullVar === "object"); // true
console.log(nullVar === null);            // true
console.log(nullVar === undefined);       // false
console.log(nullVar == undefined);        // true
console.log(1+null);                      // 1
console.log(1+undefined);                 // NaN

An attempt to access any property of null or undefined leads to ReferenceError. One can check for null or undefined in one shot using equality operator “==”:

if (someVar == null) ... // true if someVar is undefined or null

However, simply writing if (!someVar) is not recommended: the condition will be true not only for null and undefined, but also for false, the number 0, the empty string "" and Number.NaN.

Dead

Temporarily dead variables are an addition of EcmaScript6: these are variables declared via let or const keywords in the lines preceding their declaration:

function foo() {
   // x is in a "temporarily dead zone" (TDZ) here
   let x; 
   // x has value of undefined here
}

“Normal” variables are “hoisted” to the top of their scope, but “let” and “const” variables are not. Any access to a let or const variable prior to its declaration is not allowed. This includes the typeof operator.

console.log(declaredLater===undefined);        // true
console.log(typeof notDeclared==="undefined"); // true
console.log(typeof dead);                      // ReferenceError
var declaredLater = 42;
let dead;

Conclusion

To be fair, there are situations when it helps to distinguish “a value of null” and “no value at all”. E.g. when sending partial updates, null may mean “replace this field with null” and undefined may mean “do not touch”. When dealing with default function parameters: undefined means “use default value”, and null means “use null”. In the old days of COM we had VT_NULL, VT_EMPTY, and also vtMissing, which actually is a third, different value.

Still, JavaScript situation is a mess. The differences between the four “no-value” states are not intuitive, to put it mildly, and there are no explicit checks for them. I wish we had something like isDeclared(symbol), and isInTdz(symbol), but alas: the best we can do is to rely on confusing side-effects. Hence, the WTF.

Leave a Reply

Your email address will not be published. Required fields are marked *