Essential JavaScript: Mastering null vs undefined
Mastering null
and undefined
in JavaScript is often underrated. Because null
and undefined
get coerced to boolean false
, developers who are new to JavaScript often getaway by treating both these values as false
or not-a-good-value
. But mastering how these two values mean two completely different things helps you write bug-free code instead of relying on guesswork.
(To simplify, we will use the terms variable
and identifier
interchangeably.)
A tale of two nothings
Both null
and undefined
are special non-value values
to represent nothingness. By the end of this article, you will understand what we mean by “nothingness,” and two different types of “nothingness” that null
and undefined
primitive values represent.
Most programming languages use a single non-value
value, which is often called null
to represent that a reference type has no value associated with it. But JavaScript is special! And that is why you are reading this deep-dive on a seemingly simple concept.
Before we get to null
and undefined
, let’s understand what happens when we use a variable that’s never declared.
undeclared: non-existence
On line 4, the variable universe
does not exist. If you run this code in the console, you will see an error like the following:
ReferenceError: universe is not defined
That’s fair given we haven’t declared universe
anywhere. This is what we mean by undeclared variables. JavaScript engine throws a ReferenceError
when it’s unable to find an identifier in all of the accessible scopes. There is a nuance here when we try to assign a value to an undeclared variable in non-strict mode
.
The following code will create a global variable universe
in non-strict mode
.
universe = {};
Let’s safely ignore this nuance in this article and simplify our statement:
When the JavaScript engine is unable to find a variable, it throws a ReferenceError.
Now let’s add one more line(#5) to the above code.
In the above snippet, line 5 never executes. Line 4 throws an error and halts the JavaScript execution right there. This is a subtle but critical detail as you are going to learn in the following sections. It may surprise you to know that in my interviews I often see junior JavaScript engineers answer they will see the alert from line 5. I can always paraphrase their reasoning as “JavaScript is very forgiving!”
uninitialized: existence without a value
Now let’s declare our intention that we will create a universe
sometime in the future.
In this snippet, universe
is declared on line 1, but we haven’t assigned any “explicit” value to it. When we try to access its value on line 2, undefined
is printed to the console. That’s not bad given we just declared a variable and never assigned any value to it. We expect that some mystical JavaScript supreme-being will create a universe in the future when it’s time for the big-bang.
If
uninitialized
were the word used forundefined
, a lot of confusion would have been avoided as you are going to realize in the following sections.
All that we have got now is an intention that we may create a universe
in the future. It holds no value as of now. It’s not even an “explicit” empty value. It’s emptiness beyond emptiness!
undefined
represents a lack of an explicitly assigned value in a variable or in other words, a missing value.
Quoting verbatim from the ECMAScript® 2020 Language Specification:
undefined value
primitive value used when a variable has not been assigned a value.
It’s time for that big-bang to happen!
In this snippet, lines 4 to 6 create a new object and assign it to the universe
variable. Now our universe
variable has an object value.
The key takeaway here is that JavaScript assigns the primitive value
undefined
when a variable doesn’t have an explicitly assigned value.
But what if we do var universe = undefined;
explicitly? Continue reading to find the answer!
null: intentionally empty
null
is a primitive value to represent an “intentional” absence of any other assigned value.
// Intentionally empty
var universe = null;
In this example, we intentionally indicate that the universe
exists, but it is initialized “explicitly” with an empty value
represented by null
. It is a special value to represent it has no value :) That’s an existential paradox in the JavaScript universe!
In the above snippet, line 3 outputs null
as expected. Lines 6 to10 work like the example we have seen before with the undefined
.
Quoting verbatim from the ECMAScript® 2020 Language Specification:
null value
primitive value that represents the intentional absence of any object value.
If you have read the above sentence keenly, you would have noticed that null
actually represents the intentional absence of an “object value.”
Because JavaScript uses dynamic types, when a variable is declared and is not assigned a value, there is no way to differentiate it between the primitive types
and the object type
. So as per the specification null
represents an absence of an object value.
JavaScript never implicitly assigns
null
to anything; programmers have to explicitly assignnull
.
JavaScript vs Java
Empty values are not specific to JavaScript alone. In traditional object-oriented programming languages like Java, null
represents an uninitialized value for object types. Primitive types like int
, boolean
, etc. have default values that are of primitive type. For example, a boolean
variable in Java is initialized with false
.
JavaScript uses dynamic types, or in other words a variable can hold both primitive types and object type.
In summary, we have learned that the primitive value
null
is used to represent the explicit absence of an object value, while the primitive valueundefined
is used to represent no assigned value.
null and undefined are falsy values
There is one thing that null
and undefined
share in common: both null
and undefined
are coerced to boolean false
when encountered in a Boolean context. Both undefined
and null
values are considered as nullish
in JavaScript.
In the following code snippet, neither of the if conditions
evaluates to boolean true
.
var universe1;
var universe2 = null;if (universe1) {...};
if (universe2) {...};
This is why you very often see code like the following:
if (!state) {
state = initialize();
}
undefined === null vs undefined == null
// false
undefined === null// true
undefined == null
null and undefined with function parameters
JavaScript functions love undefined
! They share an immortal bond!
When a function is called with fewer parameters than it formally expects, missing values will automatically become undefined
. Let’s see a simple example.
In the above snippet, line 5 works as expected.
But line 6 is more interesting! We call sayHello
without passing any value for the parameter msg
. Since msg
is a local variable to the function sayHello
, and there was no value passed to it, the JavaScript engine initializes its value with undefined
.
Now let’s see something even more interesting.
In the above snippet, line 5 and line 6 both print undefined
. Line 6 explicitly passes undefined
. But in the sayHello
function, there is no way to distinguish between the missing parameter value(line 5) vs. using undefined
as the parameter value(line 6).
But on line 6, what is undefined
exactly? We will answer this important question soon.
Line 7 calls sayHello
with null
to represent an explicit absence of a value. JavaScript doesn’t replace it with undefined
because it's an explicit empty value.
default values for function parameters
JavaScript allows default values for function parameters.
In the following snippet, we have modified the sayHello
function to have a default value for msg
.
Line 5 works as expected. On line 6, we are calling sayHello
without passing any value to msg
. Unlike the previous code snippet where msg
was initialized with undefined
, in this example msg
gets the default value as “JavaScript!”.
On line 7, we are calling sayHello
by explicitly passing undefined
to msg
parameter. Even then, JavaScript fills in the default value into msg
.
So lines 6 and 7 are technically the same.
Line 8 is interesting as we are calling sayHello
with null
. In this case, we are intentionally indicating to the JavaScript engine that we want an explicit empty value for msg
. So JavaScript doesn’t fill in msg
with the default value.
In summary, function parameters are initialized with the default values only when a parameter has a missing value or primitive value
undefined
is passed in explicitly.
functions and return values
A JavaScript function can take zero or more inputs, and it can optionally return an output back to the function’s caller.
In the snippet below, lines 1 to 4 define a simplified add
function that takes two numbers and returns sum of those two numbers.
On line 5, we store the return value into result
and line 6 works as expected.
Lines 8 to 9 define the sayHello
function that we have been using. Line 12 works as expected.
On line 14 we store the return value of the sayHello
function call into result
.
But sayHello
doesn’t have a return statement
. So what’s in result
then?
JavaScript returns undefined
by default when a function doesn’t return any other value. If a function returns null
explicitly, then null
is returned, not undefined
.
An empty return
statement is valid in JavaScript. So the following code returns undefined
as well.
function sayHello() {
console.log("Hello!");
return;
}
encounters with undefined in the wild
There are a few places where you would encounter undefined
very often.
Non-existent properties on objects
In the snippet shown below, line 5 tries to access a non-existent property on the universe
object. JavaScript fills in the missing property lookups with undefined
. Please note line 5 does not throw any error.
If you want to know if a property exists with undefined
as its explicitly assigned value vs. a property that doesn’t exist at all, prefer using the hasOwnProperty
method from Object.prototype
.
What if we tried universe.age.years
? Continue reading to know the answer.
Non-existent indexes in Arrays
In the snippet shown below, whenever we try to access an item at an array index that never had a value set, JavaScript treats us with theundefined
value.
object and array destructuring assignments
Object and array destructuring assignments work much like the above example.
enter the void
JavaScript has a unary operator void
which evaluates an expression and returns undefined
.
// undefined
void 0;
void "Hello";
void (2 + 2);// "undefined"
typeof void true;
encounters with null in the wild
As we have already learned, JavaScript never implicitly initializes a value with null
. But here are a few places where you may encounter null
.
null in the prototype chain
All objects in JavaScript inherit from Object.prototype
, which inherits from
the null
object.
// null
Object.getPrototypeOf(Object.prototype)
regex matching
When a regular expression matching fails, it returns null
to indicate there were no matches found.
// null
console.log(/abcd/.exec("xyz"));
null is a keyword but undefined is not
null
is a keyword in JavaScript. So it can not be used for identifier
names.
The following code throws a SyntaxError
on line 2.
Unlike null
, undefined
is not a keyword in JavaScript.
The following code works as expected, though you may be shaking your head in disbelief!
Also, the following code which runs in global context is allowed and it will not throw a SyntaxError
. However, there is a nuance that we are going to see in the next section.
var undefined = 10;
global object property named “undefined”
As if all this wasn’t already confusing enough, JavaScript has an identifier
named “undefined” on the global object
whose value is primitive value undefined
.
console.log(undefined);
console.log(window.undefined);
In the above code snippet, undefined
in line 1 is being referenced like any other variable on the global object
.
Line 2 is explicitly calling it on the window
object, which is an alias for the global object
in the browser environment.
If only JavaScript stopped there! What happens when someone does the following?
undefined = "evil!"; //?
strict mode
to the rescue! Above line throws no errors and it has no effect when the code is running in strict mode
. In non-strict mode
, global undefined
gets the new value!
In most modern browsers with ECMAScript 5 support, global undefined
is a non-configurable, and non-writable property.
How not to test for undefined
From the above example, we can conclude that “undefined” property on the global object
is not reliable.
var universe;if (universe === undefined) {}
In normal conditions, the above code seems harmless. But this snippet can fail for either of the following two reasons:
- If the above code runs in the global scope, then the global “undefined” variable could have a wrong value in non-strict mode or in very old browsers.
- If the above code runs inside a local scope like a
function
, then that scope can have anidentifier
named “undefined” and it can contain a value other than the primitive valueundefined
.
Then how do we test for undefined
? Following sections, deep dive into this and a few other related topics.
Checking for non-existence?
What if we wanted to check if the universe
already exists, and not create a new one if it does? Let’s try that out.
The above snippet throws a ReferenceError
because a variable named universe
doesn’t exist in any reachable scope.
So how do we solve this chicken-and-egg problem of creation then?
Luckily we have the typeof
operator to the rescue!
typeof operator and undefined
In short, typeof
operator returns the type of the expression it is applied to using the following syntax:
typeof
expression
Returns a string value representing the type of the value that the expression is evaluated to.
typeof
always returns a string.
A few examples using typeof
:
// "number"
typeof 1;var a = "Hello World!";
// "string"
typeof a;var universe;
// "undefined"
typeof universe;var universe2 = undefined;
// "undefined"
typeof universe2;universe = {};
// "object
"typeof universe;
Now an interesting and often the most confusing part of typeof
operator is that it returns undefined
for any undeclared identifiers.
// no ReferenceError is thrown
// "undefined"
typeof nonExistentUniverse;
This is where a lot of confusion around undefined
stems from. typeof
operator, as invaluable as it is, doesn’t differentiate between a variable that’s never declared vs. a variable that’s declared but has undefined
as its value.
Ideally typeof should have returned something like undeclared
when we used it on nonExistentUniverse
.
Going back to our original question of how we check for non-existence without running into ReferenceError
, well, you know the answer now!
Unfortunately, there is no easy way to distinguish between an undeclared variable vs. a variable with no assigned value. We can employ a try/catch block, but it doesn’t look elegant.
More often than not, we don’t need to know if a variable is undeclared.
checking for null
Checking if a value is null
or not is very easy. One easy thing at last!
var universe = null;
if (universe === null) {}
Please note the usage of ===
which is a strict equality operator in JavaScript.
typeof operator and null
This is one of the most hated mistakes in the JavaScript language implementation.
// "object"
typeof null;
typeof
operator on null
returns “object”. Ideally, it should have returned “null”.
So if you want to make sure something is an object, just checking for typeof something === “object”
is not enough as it will be true for null
.
properties of undefined and null
Well, that was a trick heading! Don’t worry! There is nothing more to remember about undefined
and null
. You guessed it right! They don’t have any properties. One of the easiest things! Wasn’t it?
undefined and null in arithmetic operators
Let’s see a quick code snippet.
var num1;
var num2 = null;1 + num1; // NaN
1 + num2; // 1
Why is it so? When JavaScript has to do type conversion on any value to a number, it follows an algorithm.
That algorithm states that null
must be converted to 0 while undefined
must be converted to NaN.
You can read more about it from the spec here:
https://www.ecma-international.org/ecma-262/#sec-tonumber
quick closing thoughts
Ideally, we should strive for code that avoids explicit null
and undefined
values. But here are some closing thoughts.
Non-existent properties on object vs explicitly empty
In the above snippet, user1.email
doesn’t even exist while user2.email
exists with an undefined
value. When we use user1.email
and user2.email
, there is no easy to way distinguish between whether it exists or not. You will have to use hasOwnProperty
or some other mechanism.
user3.email
is explicitly set to null
. So we can always guarantee that it exists.
Same goes for arrays
as well.
function returning null
As we already learned, every function returns undefined
when no other value is returned from that function. If you really want to indicate that you are explicitly returning an empty or invalid value, then prefer returning null
.
Congratulations! Now you are an expert in using null
and undefined
! Please share this article and clap if you enjoyed reading it.