JavaScript Features That Are Introduced In ES2020, ES2021

Let us see mostly used new additions:

  • Dynamic Import
  • Optional Chaining Operator ?
  • Nullish Coalescing Operator ??
  • Logical Assignment Operators && = , || &
  • Private Class Variables
  • Promise.allSettled
  • Promise.Any
  • .replaceAll

◉ Dynamic Imports

That means we do not have to import everything before, and we can import the dependencies only when we need them.

and we can import dependencies dynamically with async/await. As a result, the performance of the application improves by loading the modules at runtime.

How does it improve performance? With the conventional module system, we used to import the modules statically at the beginning of the program. Whether we need them now or later, we have to import them first. Also, all the code from an imported module is evaluated at the load time. Thus, it slows down the application unnecessarily. Why? Because it downloads the imported modules and evaluates the code of each module before executing your code.

Let us see an example.

if (calculations) { 
const calculator = await import('./calculator.js');
const result = calculator.add(num1, num2);

In the above code snippet, you can see that we only import the calculator module when we want to perform calculations. As a result, we do not slow down the application unnecessarily by loading all the code before the runtime. Therefore, Dynamic imports is a handy addition.

◉ Optional Chaining Operator

“Shorter and simpler expressions when accessing chained properties when the possibility exists that a reference may be missing.” Source: MDN

Using the Optional Chaining Operator, we can access deeply nested properties from an object. If the property exists, the operator returns its value. If the property does not exist, the operator returns undefined.

const person = {
name: "Catalin Pit",
employer: {
name: "Catalins Tech",

The above code snippet illustrates an example of accessing deeply nested object properties. However, we can use it on arrays, and function calls as well. Below, in the code snippet, you can see that we check if the array exists, and if it does, we access the third value. Also, you can see checking if a function from an API exists, and if it does, it calls it.

const allowedValues = [1, 5, 10, 13, 90, 111];
// functional call
const response = myAPI.getData?.();

In conclusion, the optional chaining operator is handy, and it also helps us make the code more readable and shorter.

◉ Nullish Coalescing Operator

“The nullish coalescing operator (??) is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.” Source: MDN

Basically, the Nullish Coalescing Operator allows us to check if a value is null or undefined, and provide a fallback value if that is the case. Let us see an example:

let score = 0;
let pass = score ?? 60;

In the above code snippet, the value of pass is 0. The reason is that the ?? operator does not coerce zero to a “falsy” value. The variable pass only gets 60 assigned if the variable score is undefined or null.

However, what is the difference between double pipes “||” and this operator? When using the double pipes “||”, it always returns a truthy value which might lead to some unexpected results. When using the nullish coalescing operator, we can be more strict and thus only allow the default when the value is null or undefined.

For instance, let us say we have the following code:

let score = 0;
let pass = score || 60;

In the above example, the value 0 is treated as a falsy value when using ||. In the above code snippet, the value of pass is 60, which is not what we want. Therefore, the double question mark allows us to check if a variable is nullish, which means if a variable is either undefined or null.

Logical Assignment Operators

Logical assignment operators provide a combination of logical operators and assignment expressions. ES2021 introduces two of these new operators.

  • ||=
  • &&=


||= provides a nice shorthand for us to provide an alternative value if the initial value is empty.

If the first value is NOT truthy(falsy), the second value will be assigned to it.

In the first example below, as a is truthy, a remains equal to “hello”.

let a = “hello”;
a ||= 10;
console.log(a); // hello

In the second example, found below, as b is falsey the value is then set to “world”.

let b = “”;
b ||= “world”;
console.log(b); // world


&&= provides a nice shorthand for us to override a value if a previous value was defined.

If the first value is truthy, the second value will be assigned to it.

In the first example below, as a is truthy, a is then set to “hello”.

let a = true;
a &&= “hello”;
console.log(a); hello

In the second example below, as b is falsey, b remained equal fo false.

let b = false;
b &&= “world”;
console.log(b); false

◉ Private Class Variables & Methods

ES2015 introduced Classes to JavaScript but lacked the ability for users to define private methods.

ES2021 finally fixes this omission by introducing private methods and private fields. To indicate a method or field is private you simply need to use a hash (#) before the name.

A private variable that cannot be accessed outside a class.

For instance, #firstName is a private variable that cannot be accessed outside a class.

Trying to call the variable outside of the class, results in a SyntaxError.

class Person {
#firstName = "Catalin";
const person1 = new Person();
person1.getFirstName() // "Catalin"
console.log(person1.firstName); // Returns "undefined"
console.log(person1.#firstName); // Returns "Uncaught SyntaxError: Private field '#firstName' must be declared in an enclosing class"

In the code above, you can see a private class variable in action. Trying to access the variable firstName outside the class, we get an error. Therefore, the addition is handy when we do not want to expose data outside a class.

2. Promise.any

The Promise.any method takes an array of Promises and will return as soon as the first one is Fulfilled.

This is very similar to the Promise.race method, however, it has one key difference. While Promise.race will return if any of the promises are Settled, Promise.any will return only if one of the promises is Fulfilled.

The difference between Settled and Fulfilled is that for a promise to be fulfilled, it should not error however for a promise to be settled it just needs to return, regardless of whether the response is either a success or error.

To use Promise.any is as simple as passing it an array of promises that you want to wait for one to resolve as seen here.

const promise = (delay) => new Promise((resolve) => {
setTimeout(() => resolve(`${delay} milliseconds`), delay);
const promises = [promise(50), promise(40), promise(30)];
const resolved = await Promise.any(promises);
console.log(resolved); // 30 milliseconds

In this example, the third promise would resolve first as it only has a 30-millisecond timeout.

3. Promise.AllSettled

The new method, Promise.allSettled() waits for all promises to be settled. That is, it takes an array of Promises, and it only returns when the promises are settled, either rejected or resolved.

When this function returns, we can loop over each Promise and see whether it was fulfilled or rejected, and why. Let us see an example of this function in action.

const promise1 = Promise.resolve(5);
const promise2 = Promise.reject("Reject promise");
const promises = [promise1, promise2];
.then(results => console.log(`Here are are your promises results`, results))
.catch(err => console.log(`Catch ${err}`));

The above code returns an array of objects, and each object represents a Promise. The object has a status and a value if the Promise is fulfilled, or status and a reason if the Promise is rejected. Therefore, Promise.AllSettled is useful when you want to complete all the promises, irrelevant whether they are rejected or fulfilled.

4. String.prototype.replaceAll

The first new feature I want to touch on is String.prototype.replaceAll() which replaces all occurrences of a string with another string value.

Prior to replaceAll() the .replace() the method only replaced the first instance of a pattern by default, with multiple replacements requiring breaking out regular expressions. Now when we use replaceAll() we will see all occurrences being replaced.

const catPhrase = 'A cat sat on the cat mat';
const dogPhrase = catPhrase.replaceAll('cat', 'dog');
console.log(dogPhrase); // A dog sat on the dog mat



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Anil Kumar

Anil Kumar

Experience with Front-end Technologies and MERN / MEAN Stack. Working on all Major UI Frameworks like React, Angular.