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:
- Not declared
- Not initialized or explicitly set to
undefined
- Explicitly set to
null
- 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");
console.log(notDeclared===undefined);
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");
console.log(notInitialized===undefined);
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");
console.log(nullVar === null);
console.log(nullVar === undefined);
console.log(nullVar == undefined);
console.log(1+null);
console.log(1+undefined);
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) ...
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() {
let x;
}
“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);
console.log(typeof notDeclared==="undefined");
console.log(typeof dead);
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.
CodeProject