Essential JavaScript: Mastering null vs undefined

Chandra Gundamaraju
12 min readSep 8, 2020

--

Credits: Edited version of https://unsplash.com/photos/vw3Ahg4x1tY

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 for undefined, 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 assign null.

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 value undefined 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:

  1. 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.
  2. If the above code runs inside a local scope like a function, then that scope can have an identifier named “undefined” and it can contain a value other than the primitive value undefined.

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.

--

--

Chandra Gundamaraju

For my day job @Salesforce, I write code for machines; But in my free time, I pursue this wild hobby of writing for humans. {Learn | Code | Read | Write}