JavaScript — Undefined vs. Null (Rant.js)

Welcome to Rant.js, a new series I’m starting based on short, mostly opinions, on anything that interests me in the JavaScript world. These articles, while not entirely factual, may provide an interesting look into the day-to-day thought process of a JavaScript developer. These rants mostly stem from discussions with peers on Slack or GitHub.

Why Does This Even Matter?

Do we really need to talk about this, does it even matter? They’re both falsy values that behave extremely similarly, isn’t this a little pedantic? I don’t think so.

Declared Vs. Undeclared

To start, let’s take a moment to talk about declared vs undeclared values as they’re a little tricky within JavaScript. To make things confusing, undefined can be both a declared and an undeclared value. In the following example we show 2 variables with undefined values, one is declared and the other is undeclared. The undeclared variable will throw a ReferenceError stating that the variable is not defined, the other simply returns undefined .

While the undeclared variable throws an error, it still holds the same type as the declared variable. See the following example using the above variables:

Well That’s Confusing…

Is the type situation better for a null value, is that why I prefer it? Not exactly, it has its own issues to deal with… great! Consider the following:

That’s odd, when you check the type of null it returns that it is an object. Does this mean null is an object and is not one of JavaScript’s six built-in primitives types? Wrong! It’s a primitive type and is not an object, this is a near 20 year old bug with JavaScript that most likely won’t ever be fixed due to the harm it would cause to programs that have adjusted for it.

How Do We Type Check For Null?

The situation above makes things a little tricky when you want to explicitly check for a null type. Thankfully since null isn’t really an object, it’s the only “object” that is a falsy value, empty objects are truthy. We can use this check along with a type check to check for a its type:

While a little more to write, this is the only way we can explicitly check for a declared empty value. As shown before, the undefined type can be returned on declared and undeclared values. If you’re concerned about having to write the extra conditional, create a simple null checking function:

Isn’t This An Opinion Piece?

Yes, but I felt that a little information was needed to drive the point home, back to the original topic.

JavaScript Already Decided For Us

We have 2 different types that can represent empty values, which should we choose? JS already chose that for us, most issues are caused due to an object or array that evaluates to undefined . This ends up throwing an error when attempting to access object properties or calling object/array methods. See below:

With every added level of depth, the likelihood that an error could be thrown is increased. This is because an error will be thrown if any parent property in the chain evaluates to a non-object.

Intentionally Empty

As the above scenario happens fairly frequently, we need a way to differentiate empty values caused by errors and empty values that are intentionally empty. This is where null comes into play.

Consider the following example:

This may look innocent and easy to remember, but sometimes objects are imported within multiple files. On top of this, they may be passed through multiple functions. Original values may not be immediately obvious, especially to someone unfamiliar with your codebase (freelancers & consultants).

It would be easy to do the following and assume something broke during data processing rather than the value was intentionally assigned as undefined:

If I saw the following output, I would assume this empty null value was intentional and would not waste my time investigating a possible issue:

A Better Alternative

When possible (see Polymorphic Values), it is better to represent empty values as an empty version of its expected type. This prevents array, object, and string methods from throwing an error when they’re presented with a null or undefined value.

Compared to assigning the same variables with null values:

Polymorphic Values

The above technique works great in most situations, but sometimes we don’t have that much fine grained control of how our values are assigned. For example, a variable’s value could be set directly from the response from an API. This API could return different types depending on parameters passed or other situations outside of the developer’s control. In this situation, it’s best to set an initial value of null and null check downstream.

EXTRA: Safely Access Deep Object Properties

A common method to safely access object properties is to use a ternary. This unfortunately becomes a little cumbersome when accessing properties that are multiple levels deep.

We can define a function to safely access object properties. If any property in the chain can not properly evaluate, it will return null. This first argument is the object we’re attempting to access properties on. The second argument is a string of props, split by periods. We’ll first define an isUndefined method to match our isNull method from earlier.

Now we define our get function:

The above example canSafelyAccessNestedProperty can be converted using our get function like so:

We can save a getter to a specific object by only supplying the first argument. This is useful when you need to access an object multiple times with different property chains.

Feedback? Words of encouragement? 🎉

I’m glad you made it, hopefully you learned a few patterns or pieces of information to use in the future. I’m always looking to improve my articles, if you would like to leave feedback (I would love it if you did!) you can find the Google Form here. It’s very short, I promise!