Mastering JavaScript: optional chaining and nullish coalescing

Mastering JavaScript: optional chaining and nullish coalescing

JavaScript is a popular programming language widely used in web development, and it empowers developers to build complex and interactive applications. As with any language, there are always new techniques and features that developers can learn and master to improve their skills and productivity.

Among these advanced JavaScript techniques are Optional Chaining and Nullish Coalescing. These features were introduced in ECMAScript 2020 (ES2020) and they provide a clear and concise way to access and manipulate values within objects and arrays.

They also help to minimize the amount of code required to handle null and undefined values, which can often lead to errors and bugs in JavaScript applications.

In this article, we will explore Optional Chaining and Nullish Coalescing in detail and learn how to use these techniques to write more efficient and reliable code in JavaScript. We will also look at some practical examples of how these features can be used in real-world scenarios.

Optional Chaining: what it is and how it works

Optional Chaining is a feature in JavaScript that allows developers to safely access the properties of an object without having to manually check if the object is null or undefined. This can be particularly useful when working with complex, nested objects.

The symbol used to denote Optional Chaining in JavaScript is the ?. (question mark followed by a period).

Prior to Optional Chaining, developers often had to use the && (logical AND)operator to check if an object was null or undefined before accessing its properties. For example, consider the following code:

let obj = {
  a: {
    b: {
      c: 'hello'
    }
  }
};

let value;
if (obj && obj.a && obj.a.b && obj.a.b.c) {
  value = obj.a.b.c;
}

This code checks if obj is defined and not null, and if it is, it checks if obj.a is defined and not null, and so on. If any of these checks fail, the value variable will not be assigned a value.

With Optional Chaining, we can rewrite the above code as follows:

let value = obj?.a?.b?.c;

This syntax works by first checking if obj is defined and not null. If it is, it accesses the a property of obj. If obj.a is defined and not null, it accesses the b property of obj.a. If obj.a.b is defined and not null, it accesses the c property of obj.a.b. If any of these checks fail, the whole expression returns undefined without throwing an error.

Optional Chaining can also be used to call functions. For example:

const person = {
  name: 'Jane',
  age: 30,
  car: {
    make: 'Toyota',
    model: 'Camry',
    getInfo: function() {
      return `${this.make} ${this.model}`;
    }
  }
};

const carInfo = person?.car?.getInfo(); 
console.log(carInfo); // "Toyota Camry"

In this example, the person object has a car property, an object with a getInfo method that returns a string with the car's make and model. We can use the Optional Chaining operator (?.) to access the car property and then call the getInfo method. If the person object or car property is undefined, the expression will return undefined, and no error will be thrown.

Optional Chaining is a handy feature that can help simplify and improve the readability of your code. It is supported in modern browsers and can also be used with transpilers such as Babel](babeljs.io) to provide support in older browsers.

Understanding nullish coalescing in JavaScript

JavaScript has two types of null values: null and undefined. null represents an intentional absence of a value, while undefined represents an uninitialized or missing value. Sometimes, you might want to use a default value if a variable is either null or undefined. This is where 'Nullish Coalescing' comes in.

Nullish Coalescing is a feature in JavaScript that allows you to check if a value is null or undefined; if it is, use a default value instead.

The symbol used to denote Nullish Coalescing in JavaScript is the ?? (double question mark).

This operator checks if the value on the left side is null or undefined. If it is, it returns the value on the right side; otherwise, it returns the value on the left side. It is very useful when you want to check if a variable has a value and provide a default value. It also helps eliminate the need for multiple lines of code that would have been used to check for null or undefined values, thereby making it more readable and clean.

Let's take a look at a little example:

const name = null;
const defaultName = 'John Doe';

const displayName = name ?? defaultName;

console.log(displayName); // Output: "John Doe"

In this example, the displayName variable is assigned the value of defaultName because the name variable is null. If the name variable had been undefined, the displayName variable would also have been assigned the value of defaultName.

One thing to note is that the Nullish Coalescing operator only considers null and undefined as falsy values. All other values, including empty strings and the number 0, will be considered truthy. Here's an example to illustrate this:

const count = 0;
const defaultCount = 10;

const displayCount = count ?? defaultCount;

console.log(displayCount); // Output: 0

In this example, the displayCount variable is assigned the value of count, 0, because the Nullish Coalescing operator(??) only considers null and undefined as falsy values.

The displayCount variable would have been assigned the value of defaultCount if we had used the logical OR operator (||) , this is because when using this operator, 0 is always considered as a falsy value.

The Nullish Coalescing operator also can be helpful in situations where you want to use a default value if a variable is null or undefined, but you don't want to use the default value if the variable is an empty string or the number 0.

Here's an example of how you can use the Nullish Coalescing operator with a function argument:

function getUserName(user) {
  const name = user?.name ?? 'John Doe';
  return name;
}

const user1 = { name: 'Jane Doe' };
console.log(getUserName(user1)); // Output: "Jane Doe"

const user2 = {};
console.log(getUserName(user2)); // Output: "John Doe"

const user3 = null;
console.log(getUserName(user3)); // Output: "John Doe"

const user4 = undefined;
console.log(getUserName(user4)); // Output: "John Doe"

In this example, the getUserName function takes an object as an argument and returns the name property of the object if it exists. If the name property is null or undefined, the Optional Chaining operator( ?.) will return null or undefined, and the Nullish Coalescing operator (??) will use the default value of 'John Doe'.

The getUserName function is then called with four different arguments:

  • user1, which has a name property with a value of 'Jane Doe'

  • user2, which is an empty object and therefore does not have a name property

  • user3, which is null

  • user4, which is undefined

For user1, the function returns the value of the name property, 'Jane Doe'. For user2, the function returns the default value of 'John Doe' because the name property is not defined. For user3 and user4, the function also returns the default value of 'John Doe' because the user argument is null or undefined.

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay suite for developers. It can be self-hosted in minutes, giving you complete control over your customer data

OpenReplay

Happy debugging! Try using OpenReplay today.

Use Cases for Optional Chaining and Nullish Coalescing

Let us dive deeply into a few practical applications of Optional Chaining and Nullish Coalescing in the real world and see how they can solve problems and improve code efficiency across various industries.

Use Case 1: Deal with missing values

Let's say we have an e-commerce website that displays a user's order history.

The feature would allow users to view their past orders, including information about the products they purchased, the date of purchase, and the order total.

The data for the orders are stored in a JavaScript object that looks something like this:

const orders = [
  {
    id: 1,
    date: "2022-01-01",
    items: [
      {
        product: {
          name: "Shirt",
          price: 30
        },
        quantity: 2
      },
      {
        product: {
          name: "Pants",
          price: 50
        },
        quantity: 1
      }
    ],
    shipping: {
      name: "John Smith",
      address: {
        street: "123 Main St",
        city: "Springfield",
        state: "Oregon",
        zip: "97477"
      }
    }
  },
  {
    id: 2,
    date: "2022-02-01",
    items: [
      {
        product: {
          name: "Shoes",
          price: 100
        },
        quantity: 1
      }
    ],
    shipping: {
      name: "Jane Doe",
      address: {
        street: "456 Elm St",
        city: "Springfield",
        state: "Oregon",
        zip: "97477"
      }
    }
  }
];

To display the order history, we can loop through the orders array and use optional chaining to access the properties of each order. For example, to display the name of the product and its quantity, we can use the following code:

orders.forEach(order => {
  order.items.forEach(item => {
    const productName = item.product?.name;
    const productQuantity = item.quantity;
    console.log(`Product: ${productName}, Quantity: ${productQuantity}`);
  });
});

If the item object does not have a product property, item.product?.name will return undefined, and the code will not throw an error.

Also, to display the shipping address of an order, we can use the following code:

const shippingAddress = order.shipping?.address?.street ?? 'N/A';
console.log(`Shipping Address: ${shippingAddress}`);

If the order object does not have a shipping property, order.shipping?.address?.street will return undefined and ?? 'N/A' will assign the value N/Ato the shippingAddress variable, thus preventing the code from throwing an error.

This way, we can use Optional Chaining and Nullish Coalescing to provide default values if a property is null or undefined.

Use case 2: Access deeply nested object properties

Suppose you have an object representing a customer record with nested properties for the customer's name, address, and phone number. The object might look like this:

const customer = {
  name: {
    first: 'John',
    last: 'Doe'
  },
  address: {
    street: '123 Main St',
    city: 'New York',
    state: 'NY',
    zip: 10001
  },
  phone: '212-555-1212'
};

If you want to access the customer's phone number, you can simply use dot notation:

const phone = customer.phone;  // returns '212-555-1212'

But if you want to access a nested property that might be null or undefined, you can use Optional Chaining to safely access the property without causing an error:

const phone = customer.phone;
const zip = customer.address?.zip;  // returns 10001

Without Optional Chaining, you would have to check whether the address property is truthy before attempting to access the zip property:

const phone = customer.phone;
const zip = customer.address && customer.address.zip;  // returns 10001

Use case 3: Provide default values

Another example of using Optional Chaining and Nullish Coalescing in a JavaScript application could be a social media platform feature allowing users to view and edit their profile information.

The data for the user's profile is stored in a JavaScript object that looks something like this:

const user = {
  id: 1,
  name: "John Smith",
  email: "john.smith@example.com",
  bio: "Software developer and avid traveler",
  social: {
    facebook: "john.smith",
    twitter: "jsmith",
    website: "https://johnsmith.com"
  },
  preferences: {
    theme: "dark",
    notifications: true
  }
};

To display the user's social media links on their profile, we can use Optional Chaining to access the properties of the social object. For example, to display the user's Facebook and Twitter links, we can use the following code:

const facebookLink = user.social?.facebook ?? "Add Facebook link";
const twitterLink = user.social?.twitter ?? "Add Twitter link";
console.log(`Facebook: ${facebookLink}`);
console.log(`Twitter: ${twitterLink}`);

Here, if the user object does not have a social property or if it doesn't have a Facebook or Twitter property, user.social?.facebook or user.social?.twitter will return undefined and ?? "Add Facebook link" or ?? "Add Twitter link"will assign the default value to the variable, thus preventing the code from throwing an error.

To allow users to edit their profile information, we can use Nullish Coalescing to provide default values when updating the user's preferences. For example, to update the user's theme and notifications preferences, we can use the following code:

function updatePreferences(user, { theme = user.preferences.theme, notifications = user.preferences.notifications } = {}) {
  user.preferences.theme = theme ?? user.preferences.theme;
  user.preferences.notifications = notifications ?? user.preferences.notifications;
}

Here, if the theme or notifications values passed to the function are null or undefined, ?? user.preferences.theme or ?? user.preferences.notifications will keep the previous value of the theme or notifications and prevent the code from assigning null or undefined to the user's preferences.

In this way, we can use Optional Chaining to safely access properties of nested objects and Nullish Coalescing to provide default values when updating data in the application.

Tips for effective use of optional chaining and nullish coalescing

Optional Chaining and Nullish Coalescing can be very useful when working with complex nested objects and data structures. Still, they also have their own quirks and edge cases of which we should be aware before using them. Let's take a look at some:

  • Be aware that the Optional Chaining operator (?.) only works with objects and will not work with primitive values such as numbers, strings, or booleans. If you try to use it with a primitive value, you will get a TypeError. Here is an example:
let person = { name: "John", age: 25 };
console.log(person?.name.toUpperCase()); // "JOHN"

let age = "Not specified";
console.log(person?.age.toString()); // TypeError: Cannot read property 'toString' of undefined

In the above example, using the Optional Chaining operator to access the name property of the person object works as expected, but trying to use it to access the age property of a string variable throws a TypeError.

  • You can also use optional chaining in combination with the destructuring assignment to extract multiple properties from an object in a concise and readable way. For example, instead of writing
let street = user?.address?.street;
let city = user?.address?.city;

You could use the following:

let { street, city } = user?.address || {};
  • When using the Optional Chaining operator on a function call, be aware that if the function returns null or undefined, the entire expression will also evaluate to null or undefined. So it's important to check the return value of the function.
let user = { name: "John", age: 25 };
let getAge = () => user.age;
console.log(user?.getAge()); // 25

let getAddress = () => null;
console.log(user?.getAddress()); // null

In the first example, the function getAge returns the value of user.age, so the Optional Chaining operator works as expected. But in the second example, the function getAddress returns null, so the Optional Chaining operator also evaluates to null.

  • Understand that when using the Nullish Coalescing operator (??) with falsy values. The operator only returns the default value when the variable is null or undefined, not when it’s "falsy" (false, 0, "", etc.). In such cases, you should use a Conditional Ternary Operator ? : instead. For example:
let user = { name: "John", age: 0 };
console.log(user?.age ?? 25); // 0
console.log(user?.address ?? "Not specified"); // "Not specified"

In this example, the age is falsy but not null or undefined, so the ?? operator will not work and will return the value of user.age. But in the case of user.address, either null or undefined, the ?? operator will return the default value Not specified.

  • When using the Nullish Coalescing operator with objects, be careful when the default value is also an object because JavaScript objects are compared by reference, not by value.
let user = { name: "John", age: 25 };
let defaultAddress = { street: "Unknown", city: "Unknown" };
console.log(user.address ?? defaultAddress); // { street: "Unknown", city: "Unknown" }

user.address = defaultAddress;
console.log(user.address ?? { street: "Unknown", city: "Unknown" }); // { street: "Unknown", city: "Unknown" }

In the first example, the default value is assigned to user.address, but in the second example, the default value is not assigned because it's the same object as the one in user.address, so the ?? operator returns the same object.

  • Use Nullish Coalescing when you want to provide a default value for a variable if it is null or undefined. This allows you to avoid having to use the ternary operator ? : or a default parameter in a function. For example:
const obj = {
  foo: undefined
};

// Without nullish coalescing
const value = obj.foo !== null && obj.foo !== undefined ? obj.foo : 'default value';

// With nullish coalescing
const value = obj.foo ?? 'default value';
  • Always be mindful of the order of precedence of the operators. In javascript, the ?. operator has higher precedence than the ?? operator. So let name = person?.name ?? 'John Doe' will check for person.name first, and if it's null or undefined, it will check for the default value.

So that your code behaves as expected and avoids any potential errors, It's very important to be mindful of these when using optional chaining and nullish coalescing in JavaScript because they can lead to unexpected results if not used correctly.

Conclusion

In conclusion, mastering advanced JavaScript techniques such as Optional Chaining and Nullish Coalescing can greatly improve the clarity and simplicity of your code. Optional Chaining allows for concise handling of potentially null or undefined values, while nullish coalescing allows for a default value to be assigned in the case of null or undefined. These techniques have various use cases and can be effectively utilized with a bit of practice and careful consideration. Using Optional Chaining and Nullish Coalescing, developers can write more efficient, clean, and maintainable code.

Also, as these are both relatively new features in JavaScript, they may not be fully supported in all browsers. It's always a good idea to check the current browser support for new features before using them in production code. You can use a tool like caniuse.com or check the browser's developer documentation for more information.

I hope this article brought clear light to these topics, and as always, Happy Coding.

newsletter