carlos caballero

ES2021 Features in simple examples

3 min read

In this series, we are going to show the EcmaScript features from 2015 to today.


ES2021 is the version of ECMAScript corresponding to the year 2021. This version doesn’t include as many new features as those that appeared in ES6 (2015). However, some useful features have been incorporated.

This article introduces the features provided by ES2021 in easy code examples. In this way, you can quickly understand the new features without the need for a complex explanation.

Of course, it’s necessary to have a basic knowledge of JavaScript to fully understand the best ones introduced.

The new JavaScript features in ES2021 are:

➡️ String.prototype.replaceAll
➡️ Promise.any
➡️ WeakRef
➡️ Logical Assignment Operators
➡️ Numeric separators


Currently there is no way to replace all instances of a substring in a string without use of a global regexp (/regexp/g).

const fruits = '🍎+🍐+🍓+';
const fruitsWithBanana = fruits.replace(/\+/g, '🍌');
console.log(fruitsWithBanana); //🍎🍌🍐🍌🍓🍌

A new replaceAll method has been added to the String prototype.

const fruits = '🍎+🍐+🍓+';
const fruitsWithBanana = fruits.replaceAll('+', '🍌');
console.log(fruitsWithBanana); //🍎🍌🍐🍌🍓🍌


Promise.any gives you a signal as soon as one of the promises fulfills. This is similar to Pormise.race , except any doesn’t reject early when one of the promises rejects.

const myFetch = url => setTimeout(() => fetch(url), Math.floor(Math.random() * 3000));

const promises = [

// Using .then .catch
Promise.any(promises) // Any of the promises was fulfilled.
       .then(console.log) // e.g. '3'
       .catch(console.error); //All of the promises were rejected.

// Using async-await
try {
   const first = await Promise.any(promises); // Any of the promises was fulfilled.
}catch (error) { // All of the promises were rejected


The WeakRef proposal encompasses two major new pieces of functionality:

  1. creating weak references to objects with the WeakRef class.

  2. running user-defined finalizers after objects are garbage-collected, with the FinalizationRegistry class.

These interfaces can be used independently or together, depending on the use case

A WeakRef object contains a weak reference to an object, which is called its target or referent. **A weak reference to an object is a reference that does not prevent the object from being reclaimed by the garbage collector.

A primary use for weak references is to implement caches or mappings holding large objects, where it’s desired that a large object is not kept alive solely because it appears in a cache or mapping.

function toogle(element) {
   **const weakElement = new WeakRef(element);** 
   let intervalId = null;
   function toggle() { 
     **const el = weakElement.deref();**
     if (!el) {
        return clearInterval(intervalId);
    const decoration =;
    const style= decoration === 'none' ? 'underline' : 'none';
    decoration = style;
   intervalId = setInterval(toggle, 1000);

 const element = document.getElementById("link");

 setTimeout(() => element.remove(), 10000);

FinalizationRegistry provides a way to request that a cleanup callback (finalizers) get called at some point when an object registered with the registry has been reclaimed (garbage-collected).

You create the registry passing in the callback:

const registry = new FinalizationRegistry(heldValue => {
  // ....

Then you register any objects you want a cleanup callback for by calling the register method, passing in the object and a held value for it:

registry.register(theObject, "some value");

Logical Assignment Operators

Logical assignment operators combine logical operators and assignment expressions. There are two new operators:

  1. Or Or Equals.
  2. And And Equals.
// Or Or Equals
|   a   |   b   | a ||= b | a (after operation) |
| true  | true  |   true  |        true         |
| true  | false |   true  |        true         |
| false | true  |   true  |        true         |
| false | false |   false |        false        |

a ||= b
// Equivalent to:
a || (a = b);

// And And Equals
|   a   |   b   | a ||= b | a (after operation) |
| true  | true  |   true  |        true         |
| true  | false |   false |        false        |
| false | true  |   false |        false        |
| false | false |   false |        false        |

a &&= b
// Equivalent to:
a && (a = b);

Numeric separators

This feature allows that numeric literals will be more readable using a visual separation between groups of digits.

Using underscores (_, U+005F) as separators helps improve readability for numeric literals:

1_000_000_000           // A billion
101_475_938.38          // Hundreds of millions

const amount = 12345_00;  // 12,345 (1234500 cents, apparently)
const amount = 123_4500;  // 123.45 (4-fixed financial)
const amount = 1_234_500; // 1,234,500

0.000_001 // 1 millionth
1e10_000  // 10^10000 -- granted, far less useful / in-range...
const binary_literals = 0b1010_0001_1000_0101;
const hex_literals = 0xA0_B0_C0;
const bigInt_literals = 1_000_000_000_000n;
const octal_literal = 0o1234_5670;


JavaScript is a live language, and that’s something very healthy for web development. Since the appearance of ES6 in 2015, we’re living a vibrant evolution in the language. In this post, we’ve reviewed the features that arise in ES2021.

Although many of these features may not be essential for the development of your web application, they’re giving possibilities that could be achieved before with tricks or a lot of verbosity.