How the Nullish Coalescing Operator Works in JavaScript

JavaScript is always evolving and adding new features to make the language more expressive and convenient to use. One recent addition in the ECMAScript 2020 specification is the nullish coalescing operator, denoted by the double question mark (??) symbol.

In this in-depth guide, we‘ll take a close look at what the nullish coalescing operator is, how it works, and why it‘s a useful tool to have in your JavaScript toolkit. We‘ll walk through several code examples to illustrate the syntax and semantics of this operator and discuss some common use cases and best practices.

By the end of this article, you‘ll have a solid understanding of the ?? operator and how to leverage it effectively in your own JavaScript programs. Let‘s dive in!

What is the Nullish Coalescing Operator?

In a nutshell, the nullish coalescing operator is a logical operator that returns its right-hand operand when the left-hand operand is null or undefined. Otherwise, it simply returns the left-hand operand as-is.

Here‘s the basic syntax:

leftExpr ?? rightExpr

The ?? operator short-circuits, meaning it only evaluates the right-hand expression if needed. If the left-hand operand is not null or undefined, the right side is never even evaluated.

Let‘s look at a few quick examples:

null ?? ‘default‘; // ‘default‘
undefined ?? ‘default‘; // ‘default‘

‘Bruce Wayne‘ ?? ‘Batman‘; // ‘Bruce Wayne‘
‘‘ ?? ‘default‘; // ‘‘ (empty string)
false ?? true; // false
0 ?? 100; // 0

As you can see, when the expression on the left of ?? is either null or undefined, the result is the value of the right-hand operand. For any other truthy or falsy value, the left operand is returned as the result.

The Problem with the Logical OR Operator

You might be wondering what‘s the big deal with the nullish coalescing operator. After all, can‘t we already achieve something similar using the logical OR (||) operator?

While the || operator can be used in a similar way to provide default values, there‘s a subtle but important difference in how it behaves. The || operator returns the first truthy value among its operands.

null || ‘default‘; // ‘default‘
undefined || ‘default‘; // ‘default‘

false || ‘default‘; // ‘default‘
0 || ‘default‘; // ‘default‘  
‘‘ || ‘default‘; // ‘default‘

Do you see the issue? Values like false, 0, and ‘‘ are all falsy in JavaScript. So when used with ||, those values will be skipped and the second operand will be returned, even if that‘s not what was intended.

In many cases, we want to preserve values like false, 0 or ‘‘ if they are valid, defined values for a variable. We only want to fall back to a default if the variable is truly missing or undefined. And that‘s exactly the problem the nullish coalescing operator solves!

With ??, falsy values like false and 0 will not be coalesced:

false ?? true; // false
0 ?? 100; // 0  
‘‘ ?? ‘default‘; // ‘‘

Only null and undefined values will trigger the coalescing to the fallback value on the right-hand side. This allows for a safer, more intentional way to handle default values and avoid accidentally overwriting falsy but valid data.

Use Cases for the Nullish Coalescing Operator

Now that we understand what the ?? operator does and how it compares to ||, let‘s look at some real-world examples and scenarios where it comes in handy.

Providing Default Values

One of the most common use cases for ?? is to provide default values for variables or function parameters that might be null or undefined.

function greet(name) {
  name = name ?? ‘Anonymous‘;
  console.log(`Hello, ${name}!`); 
}

greet(‘John‘); // Hello, John!  
greet(null); // Hello, Anonymous!
greet(undefined); // Hello, Anonymous!

Here the ?? operator is used to provide a default name of ‘Anonymous‘ if the name parameter is null or undefined. This is a clean and concise way to handle missing values without resorting to a more verbose if statement or ternary expression.

Accessing Nested Object Properties

Another useful application of the ?? operator is when accessing nested properties of an object. It‘s common to have optional or potentially undefined nested structures in JavaScript objects, especially when dealing with JSON data from an API.

const person = {
  name: ‘Alice‘,
  address: {
    street: ‘123 Main St‘,
    city: ‘Anytown‘,    
  },
};

console.log(person.address.street); // ‘123 Main St‘
console.log(person.address.zipCode); // undefined
console.log(person.address.zipCode ?? ‘Unknown‘); // ‘Unknown‘

console.log(person.phoneNumber.number); // TypeError: Cannot read property ‘number‘ of undefined  
console.log(person.phoneNumber?.number ?? ‘Unknown‘); // ‘Unknown‘

In this example, we can safely access the street property of the address object using normal dot notation. But when we try to access zipCode, it returns undefined since that property does not exist.

We can provide a default value of ‘Unknown‘ for missing zip codes by using the ?? operator. This avoids returning undefined and provides a more meaningful default value.

However, trying to access number from phoneNumber will throw an error, since phoneNumber itself is undefined. To handle this case, we can combine the optional chaining operator (?.) with nullish coalescing to safely access nested properties and provide a default.

Caching and Memoization

The nullish coalescing operator can also be quite useful in caching and memoization scenarios, where we want to avoid recomputing values if they have already been calculated before.

let cachedValue;

function compute() {
  console.log(‘Computing a new value...‘);
  // complex calculation  
  return 42;
}

// Later...
cachedValue = cachedValue ?? compute();
console.log(cachedValue); // Computing a new value... \n 42

// Later still... 
cachedValue = cachedValue ?? compute();  
console.log(cachedValue); // 42

In this example, the compute function is an expensive operation that we only want to run if needed. On first invocation, cachedValue will be undefined, so the ?? operator will invoke compute() and cache its result.

On subsequent calls, cachedValue will already have the cached value 42, so there will be no need to recompute it. The ?? short-circuiting prevents the compute function from being called again unnecessarily.

Of course, there are many other caching strategies and libraries out there. But the ?? operator can be a quick and easy way to optimize repeated expensive function calls.

Nullish Coalescing and Other Modern JavaScript Features

The nullish coalescing operator fits right in with other recent JavaScript features designed to make the language more expressive and less error-prone. Two features in particular are closely related to ?? and are often used in conjunction with it.

Default Parameters

Default function parameters, introduced in ES2015, allow you to specify default values for parameters right in the function declaration. This feature largely obviates the need to use ?? for simple parameter defaults.

// Old way 
function greet(name) {
  name = name ?? ‘Anonymous‘;
  console.log(`Hello, ${name}!`);
}

// With default params 
function greet(name = ‘Anonymous‘) {
  console.log(`Hello, ${name}!`); 
}

With default parameters, we can simply provide the default value in the function signature, making the body of the function cleaner and more semantic.

However, ?? is still useful inside the function body if you need to work with other potentially nullish values beyond the parameters.

Optional Chaining

The optional chaining operator (?.), also introduced in ES2020 alongside ??, is another closely related feature. As we saw in the earlier example, optional chaining allows you to safely access nested properties of an object without worrying about null or undefined intermediate values.

const person = {
  name: ‘Alice‘,
  address: {
    street: ‘123 Main St‘,
    city: ‘Anytown‘,
  },
};

console.log(person?.phoneNumber?.number ?? ‘Unknown‘); // ‘Unknown‘

Using ?., we can safely attempt to access the number property of phoneNumber without throwing an error if either person or phoneNumber are undefined. The optional chaining short-circuits the evaluation and returns undefined if any part of the chain is nullish.

We can then combine that with ?? to provide a default value in case the final property access evaluates to null or undefined. Optional chaining and nullish coalescing are often used together in this way to safely navigate and default complex nested structures.

Nullish Coalescing Best Practices and Gotchas

To wrap up our deep dive into the nullish coalescing operator, let‘s review some best practices to follow and potential gotchas to be aware of when using ?? in your code.

Don‘t Overuse It

While the ?? operator is certainly useful, it‘s important not to overuse it or rely on it too heavily. In many cases, it‘s still clearer and more semantic to handle default values using if statements, ternaries, or default parameters.

Only reach for ?? when it genuinely improves the readability and expressiveness of your code. If a simple if statement would suffice, it‘s often better to stick with that.

Be Aware of Operator Precedence

Like the || operator, ?? has relatively low precedence in the operator precedence table. This means that ?? will be evaluated after most other operators in a complex expression.

null || undefined ?? ‘default‘; // SyntaxError: Unexpected token ‘??‘

(null || undefined) ?? ‘default‘; // ‘default‘
null || (undefined ?? ‘default‘); // ‘default‘

In the first example, the || operator is evaluated first, resulting in undefined. Then the interpreter unexpectedly sees the ?? operator and throws an error, since nullish coalescing is not allowed directly after ||.

To fix this, we need to explicitly control the order of operations with parentheses, as shown in the other two examples. When mixing ?? with other logical operators, it‘s important to use parentheses to clearly specify the intended evaluation order.

Avoid Mixing with && or ||

In general, it‘s best to avoid mixing the ?? operator with the && and || operators in the same expression. While it‘s technically possible, the different semantics of the operators can lead to confusing and hard-to-read code.

null || undefined ?? ‘default‘; // SyntaxError 

null ?? undefined || ‘default‘; // undefined
(null ?? undefined) || ‘default‘; // ‘default‘

In the first example, we get an error because mixing || and ?? without parentheses is invalid. The other two examples are valid but look confusing at first glance. The rules around operator precedence and short-circuiting make the evaluation non-obvious.

It‘s generally cleaner to handle these cases with separate, explicit if statements or ternaries to avoid any ambiguity. Mixing multiple logical operators in the same expression is often a code smell and can make the logic harder to understand and maintain.

Conclusion

The nullish coalescing operator is a small but mighty addition to the JavaScript language. By providing a concise and expressive way to handle null and undefined values, it helps us write cleaner, more robust code.

Whether you‘re providing default values, safely accessing nested properties, or optimizing expensive computations, ?? is a useful tool to have in your belt. When used judiciously and combined with other modern features like optional chaining and default parameters, it can help make your code more readable and less error-prone.

As with any language feature, it‘s important to understand the semantics and best practices around the ?? operator. Be aware of operator precedence, avoid mixing it with other logical operators, and don‘t overuse it at the expense of clarity.

By following these guidelines and examples, you‘ll be able to leverage the power of ?? in your own JavaScript projects. Happy coding!

Similar Posts