When working with JavaScript, there are two important values you will inevitably encounter and will come across in almost every JavaScript project: null and undefined. At first glance, both may seem quite straightforward and similar, but each has its own unique characteristics and pitfalls. In this article, we will explore the similarities and differences between these two values, their importance in the JavaScript language, and key aspects worth mentioning.

What is null?

null is a primitive value in JavaScript that represents the intentional absence of any object value. It's often used to indicate that a variable or property should hold no value. Similar to how NaN is a number but represents an invalid number, null is an object but represents the absence of an object. null is typically used to explicitly set a variable or property to "no value", with the intention of updating it later.

let user = null;
console.log(user); // null
console.log(typeof user); // "object"

Assume you are accepting input from a user and need to create a user object if the given input is valid. In that case, you would want to perform some checks and create the actual user object (perhaps with the intention of saving to the database) based on the results of these validations.

function addUser(email, password) {
	let user = null;
	
	if (validateEmail(email) && validatePassword(password)) {
		user = createUserObject(email, password);
	} else {
		return null;
	}
	
	saveToDB(user);
}

The above is one of the most common scenarios where you might need to use null value. We explicitly indicate that user variable is meant to hold an object but currently has no value before actually performing the check.

What is undefined?

undefined is similar to null in representing the absence of a valid value. However, the key difference is that it refers to a variable that has been declared but not yet assigned a value. It is the default state for uninitialized variables.

undefined has its own type: undefined.

console.log(typeof undefined) // undefined

Because undefined is default state for all undeclared variables, a variable automatically gets the value undefined if it's declared but not initialized.

let data;
console.log(data); // undefined
console.log(typeof data); // undefined

If you try to access the return value of a function without a return statement, you will receive undefined:

function foo() {}

console.log(foo()) // undefined

Accessing a non-existent object property will also result in undefined:

const obj = {}
console.log(obj.missingProp) // undefined

The difference betwen null and undefined

Both null and undefined signify an absence of value but their meanings and uses are different. While undefined represents the absence of a value in a variable that has been declared but not assigned, null represents an explicitly set and intentional absence of any object value.

null undefined
Type object undefined
Default value? No Yes, for uninitialized variables
Usage Intentionally represents absence of value Implicitly uninitialized

How to check for null and undefined?

Checking for null

The strict equally operator is used to check if a value is null.

let value = null;

if (value === null) {
  console.log("The value is null");
}

Please note that strict equally operator is always a better choice than the loose equality (==) operator since the latter considers both null and undefined as equal, which can lead to unintended results.

let value;

if (value == null) {
  console.log('value is null');
}

console.log(value); // undefined

When you run the code block above, the output will be as follows::

value is null
undefined

Even though the value of our variable is undefined, the loose equality operator considers null equal to undefined. Therefore, the strict equality operator (===) is always a better choice for null checks.

let value;

if (value === null) {
  console.log('value is null'); // this line won't be executed
}

console.log(value); // undefined

You can see how the loose equality operator (==) behaves with null and undefined values:

console.log(null == undefined); // true
console.log(null === undefined); // false

Checking whether a value is falsy is also not ideal, as null is not the only falsy value in JavaScript.

let value = '';

if (!value) {
  console.log('value is null');
}

console.log(typeof value); // string

When you run the code above, the output will be:

value is null
string

if condition above can be true for any falsy value.

Checking for undefined

The strict equality operator is also used to check if a value is undefined:

let value;

if (value === undefined) {
	console.log("The value is undefined");
}

You can also use the typeof operator:

let value;

if (typeof value === 'undefined') {
	console.log("The value is undefined");
}

You can use the in operator to check if a property exists in an object.

const obj = {};

if (!("prop" in obj)) {
    console.log("Property does not exist");
}

Best practices for handling null and undefined

JavaScript provides convenient features to handle null and undefined values with ease. As you spend more time writing JavaScript, you will find yourself using these features more frequently.

Optional chaining operator (?.)

The Optional Chaining operator allows you to safely access deeply nested properties without worrying about undefined or null values causing an error. It was introduced in ES2020 and has quickly become one of the most used features of the language because it simplifies checking for nested values.

const person = {
  name: 'John',
  cat: {
    name: 'Misty',
  },
};

console.log(person.cat?.name); // Misty
console.log(person.dog?.name); // undefined

Even though the dog attribute does not exist, we get an undefined value instead of a runtime error because the optional chaining operator checks for null and undefined before attempting to access the property.

We can also use it when calling methods.

const person = {
  name: 'John',
  cat: {
    name: 'Misty',
  },
};

person.cat.greet = function() {
  return `Hello, my name is ${this.name}`;
};

console.log(person.cat?.greet()); // Hello, my name is Misty
console.log(person.dog?.greet()); // undefined

It is possible to use this operator before function parentheses in case the method itself is either null or undefined

const person = {
  name: 'John',
  cat: {
    name: 'Misty',
  },
};

person.greet = function() {
  return `Hello, my name is ${this.name}`;
};

console.log(person.greet()); // Hello, my name is John
console.log(person.foo?.()); // undefined

Nullish coalescing operator (??)

The Nullish Coalescing operator (??) is used to provide a default value when a variable is either null or undefined. It was introduced in ES2020 and works as a more predictable alternative to the logical OR (||) operator when dealing specifically with null and undefined.

let person = null;
let personName = person?.name ?? 'Anonymous';

console.log(personName); // Anonymous

Before the ?? operator, the logical OR (||) operator was commonly used for fallback values. However, the logical OR operator has a broader scope as it treats all falsy values as triggers for the fallback.

const value = 0 || 100;
console.log(value); // 100 (unexpected, as 0 is falsy)

const value2 = 0 ?? 100;
console.log(value2); // 0 (expected behavior, as 0 is a valid value)

Nullish coalescing assignment (??=) operator

The Nullish Coalescing Assignment (??=) operator is a shorthand introduced in ES2021 that allows you to assign a value to a variable only if it is currently null or undefined.

myvar ??= myval

If myvar is null or undefined, it assigns the value on the right. If it already holds a value (including 0, false, ""), it does nothing.

let username;
username ??= "Guest";
console.log(username); // "Guest"

let age = 0;
age ??= 18;
console.log(age); // 0 (since 0 is a valid value)

Conclusion

null and undefined are important concepts in JavaScript, and understanding the difference between them is crucial for writing clean and robust code. Although there are some tricky parts and pitfalls, the difference is quite straightforward: you should expect undefined when variables or properties are uninitialized, while null represents the intentional absence of a value. Modern operators, such as the optional chaining operator, help you handle null and undefined values more efficiently by reducing the need for multiple conditions. As you spend more time working on JavaScript projects, you will use these values and operators more frequently and gain a better understanding of how they are powerful tools for writing robust and predictable JavaScript code.

AUTHOR
PUBLISHED 18 January 2025
TOPICS