Frontend Development 11 min read

ES2020 Optional Chaining and Dynamic Imports: Reducing Code and Boosting Performance

This article explains the ES2020 optional chaining (?.) and dynamic import() features, showing their syntax, practical code examples, compatibility notes, and how they simplify JavaScript code while improving web application performance.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
ES2020 Optional Chaining and Dynamic Imports: Reducing Code and Boosting Performance

Since the release of ES6 in 2015, JavaScript has undergone several upgrades, and the ES2020 specification introduced two game‑changing features: optional chaining (?.) and dynamic imports. Using these features can dramatically reduce the amount of boilerplate code in existing projects.

Optional Chaining

The optional chaining operator ?. works like the dot operator but stops evaluation when the left‑hand side is undefined or null , returning undefined instead of throwing an error. This allows safe access to deeply nested properties without explicit existence checks.

Syntax

Official documentation defines four forms:

obj.val?.prop
obj.val?.[expr]
obj.arr?.[index]
obj.func?.(args)

Usage Examples

Property access

let book = {
  name: "Harry Potter 1",
  price: { value: 50, currency: "EUR" },
  ISBN: "978-7-7058-9615-2"
};
console.log(book.price.value); // 50
console.log(book.weight); // undefined
// console.log(book.weight.value); // throws
console.log(book?.weight?.value); // undefined

Expression access

let userInputProperty = document.getElementById('inputProperty').value;
let userInputNestedProperty = document.getElementById('inputPropertyNested').value;
console.log(book[userInputProperty][userInputNestedProperty]); // may throw
console.log(book?.[userInputProperty]?.[userInputNestedProperty]); // safe, returns undefined if missing

Array element access

let books = [{ name: "Harry Potter 1", price: { value: 50, currency: "EUR" }, ISBN: "978-7-7058-9615-2" },
             { name: "Harry Potter 2", price: { value: 60, currency: "EUR" }, ISBN: "978-3-2560-1878-4" }];
console.log(books[1].price.value); // 60
// console.log(books[2].price.value); // throws
console.log(books?.[2]?.price?.value); // undefined

Function call

let bookModule = {
  getBooks: function() {
    console.log("Books returned");
    return true;
  }
};
bookModule.getBooks(); // "Books returned"
// bookModule.getBook(); // throws
bookModule?.getBook?.(); // undefined, no error

Important Note

Optional chaining works only on the right‑hand side of an assignment; using it on the left‑hand side results in a syntax error.

let book = { name: "Harry Potter 1", price: { value: 50, currency: "EUR" } };
book?.weight?.value = 650; // SyntaxError
book.weight = { value: 650 }; // valid

Before vs. After

Prior to optional chaining, developers needed explicit checks for each nesting level, leading to verbose code. With ?. , the same logic collapses into a single, readable expression.

// Verbose checks
if (book && book.weight && book.weight.version2 && book.weight.version2.value) {
  console.log(book.weight.version2.value);
}
// Using optional chaining
console.log(book?.weight?.version2?.value); // 690
console.log(book?.weight?.version3?.value); // undefined

Browser Compatibility

According to CanIUse, optional chaining is supported by all modern browsers, with polyfills available for older versions.

Dynamic Imports

Dynamic import() enables native, on‑demand loading of JavaScript modules at runtime, allowing code to be fetched only when needed. This reduces initial bundle size and improves page load performance.

Why Dynamic Imports?

Static import statements must appear at the top level, accept only string literals, and cannot be conditional. Dynamic imports remove these constraints, supporting variable module specifiers and conditional loading.

Static vs. Dynamic Import Comparison

Static import

import { exportAsCSV } from './export-as-csv.js';
import { exportAsXML } from './export-as-xml.js';
const dataBlock = getData();
exportCSVButton.addEventListener('click', () => exportAsCSV(dataBlock));
exportXMLButton.addEventListener('click', () => exportAsXML(dataBlock));

Both modules are loaded regardless of whether the user clicks the corresponding button, increasing the initial download size.

Dynamic import

const dataBlock = getData();
exportCSVButton.addEventListener('click', () => {
  import('./export-as-csv.js')
    .then(module => { module.exportAsCSV(dataBlock); })
    .catch(err => { /* handle load error */ });
});
exportXMLButton.addEventListener('click', () => {
  import('./export-as-xml.js')
    .then(module => { module.exportAsXML(dataBlock); })
    .catch(err => { /* handle load error */ });
});

Modules are fetched only when the user triggers the associated action, reducing the amount of JavaScript transferred on initial page load.

Browser Compatibility

CanIUse shows that dynamic import() is supported in all major modern browsers, with fallbacks available for older environments.

Both optional chaining and dynamic imports are powerful ES2020 features that let developers write cleaner, more maintainable code without sacrificing performance.

frontendJavaScriptweb performanceoptional-chainingdynamic-importES2020
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.