What’s new for JavaScript in ES2021?

OpenJavaScript 0
Reading Time: 3 minutes 🕑

Last updated: January 2, 2022.

Several new features have been introduced by the ES2021 upgrade to the JavaScript language:

We review each of these features below with live code editor examples:

.replaceAll()

Previously it was possible to apply the .replace() method to a string to replace part of its contents (syntax: stringName.replace("oldValue", "newValue")). But this only replaces the first appearance of the old value to be replaced:

const message2020 = "2020...and 2020...and 2020...and 2020";

message2021 = message2020.replace("2020", "2021");

console.log(message2021);
// "2021...and 2020...and 2020...and 2020"

ES2021 introduces replaceAll(), which replaces all instances of an old value with a new value within a string:

const message2020 = "2020...and 2020...and 2020...and 2020";

message2021 = message2020.replaceAll("2020", "2021");

console.log(message2021);
// "2021...and 2021...and 2021...and 2021"

Promise.any

In some situations, you may be waiting upon one of several promises to resolve in your code - and want to start working with the first successfully resolved promise immediately.

Promise.any() does exactly this. Promises should be passed into it in array format.

Note that a promise that resolves successfully after the first successfully resolved promise will be ignored. Promise.any only works with the first successfully resolved promise. Also, it ignores unsuccessful promises, even if the return immediately.

For example:

// Create promises

const promise1 = new Promise((resolved) => { 
setTimeout(resolved, 200, 'fast') 
} );

const promise2 = new Promise((resolved) => { 
setTimeout(resolved, 100, 'lightning') 
} );

const promise3 = new Promise((resolved) => {
setTimeout(resolved, 1000, 'slowest') 
} );

const promise4 = Promise.reject(0);


// Store in array

const promises = [promise1, promise2, promise3, promise4];

// Use promise.any

Promise.any(promises).then((result) => console.log(result));
// "lightning"

Notice that in this example, Promise4 is immediately rejected. But the result is "lightning". This is because Promise.any ignores rejected promises and works with the one that is first to successfully complete. Promise1 and Promise3 are ignored because, even though they successfully resolve, they do so after Promise2.

Numeric separators

ES2021 permits the user of underscores to improve the readability of numeric literals when coding.

For example, in JavaScript code, it is possible to write 10_000 instead of 10000.

This is purely for readability when coding. The inclusion of underscores does not change the numeric value:

const bigNumber = 1_000_000_000;

console.log(bigNumber);
// 1000000000

Logical assignment operators (&&=, ||=, ??=)

Logical assignment operators combine logical comparison and assignment. ES2021 introduces three fo these: &&=, ||= and ??=.

These are useful in checking whether a left-hand side value is truthy ( &&=), falsy (||&) or exclusively null or undefined (??=) and, if true, replacing it with a new value.

&&=

&&= checks if a value is 'truthy'. If yes, assigns a new value:

let x = 3
x &&= "Is truthy" // checks if x is truthy (returns true)
console.log(x) // "Is truthy"

||=

||= checks if a value is 'falsy'. If yes, assigns a new value:

let x = 3
x  ||= "Is truthy" // checks if x is falsy (returns false)
console.log(x) // 3

??=

??= checks if a value is null or undefined. If yes, new value is assigned:

let x = null
x ??= 10 // checks is x is null or undefined (returns true)
console.log(x) // 10

Private class methods

ES2021 allows methods within a class to be made private by prepending "#". The private method then cannot be accessed in the global scope directly:

class Login {
  printUsername() {
    console.log("Username")
  }
  #printPassword() {
    console.log("Password")
  }
}

const login = new Login()

login.printUsername() // "Username"
login.printPassword() // "Error: not a function"

However, it is still possible to access it indirectly from the global scope if another method within the class calls it:

class Login {
  printUsername() {
    console.log("Username");
  }
  #printPassword() {
    console.log("Password");
  }
    printBoth() {
    this.printUsername();
    this.#printPassword();
  }
}

const login = new Login();

login.printBoth();
// "Username"
// "Password"

Private get and set

Similarly to private class methods, get and set commands within classes can also be made private by appending #:

class Login {
  get username() {
    return console.log("Username");
  }
  get #password() {
    return console.log("Password");
  }
}

const login = new Login();
login.username; // "Username"
login.password; // Does not print anything

And similarly, these can be accessed indirectly from the global scope by a command within the class:

class Login {
  get username() {
    return console.log("Username");
  }
  get #password() {
    return console.log("Password");
  }
  get showBoth() {
    return this.username + this.#password; 
  }

}

const login = new Login();
login.showBoth;
// "Username"
// "Password"

Summary

In this tutorial, we have reviewed the new features introduced to the JavaScript language by the ES2021 upgrade.

To learn more about recent upgrades to the JavaScript language, visit our ES6+ section.