DEV Community

Faithful Ojebiyi
Faithful Ojebiyi

Posted on • Updated on

New Features in ECMAScript 2021 (with code examples)

Alt Text

In this tutorial we are going to learn about the new features we can use in the latest version of ECMAScript 2021 with some coding examples

Introduction

This Ecma Standard defines the ECMAScript 2022 Language. It is the twelfth edition of the ECMAScript Language Specification. Since publication of the first edition in 1997, ECMAScript has grown to be one of the world's most widely used general-purpose programming languages. It is best known as the language embedded in web browsers but has also been widely adopted for server and embedded applications.Introduction
This Ecma Standard defines the ECMAScript 2022 Language. It is the twelfth edition of the ECMAScript Language Specification. Since publication of the first edition in 1997, ECMAScript has grown to be one of the world's most widely used general-purpose programming languages. It is best known as the language embedded in web browsers but has also been widely adopted for server and embedded applications.

ECMA 2021

ECMAScript 2021, the 12th edition, introduces the replaceAll method for Strings; Promise.any, a Promise combinator that short-circuits when an input value is fulfilled; AggregateError, a new Error type to represent multiple errors at once; logical assignment operators (??=, &&=, ||=); WeakRef, for referring to a target object without preserving it from garbage collection, and FinalizationRegistry, to manage registration and unregistration of cleanup operations performed when target objects are garbage collected; separators for numeric literals (1_000); and Array.prototype.sort was made stable.

replaceAll

Assuming we have a constant named string

const string = "Javascript is the best web scripting language. Javascript can be used for both front end and backend";
Enter fullscreen mode Exit fullscreen mode

and we want to replace the word Javascript with Typescript
we normally would use the replace method

const string = "Javascript is the best web scripting language. Javascript can be used for both front end and backend";

console.log(string.replace("Javascript", "Typescript"));
Enter fullscreen mode Exit fullscreen mode

What if we want to replace all instances of Javascript with Typescript
That is where the replaceAll method comes to shine

const string = "Javascript is the best web scripting language. Javascript can be used for both front end and backend";

console.log(string.replaceAll("Javascript", "Typescript"));
Enter fullscreen mode Exit fullscreen mode

Alt Text

Private Methods

lets create a class named People and we have will have some methods

class People {
  showName() {
    console.log("My name is Faithful")
  }
  showAge() {
    console.log("Faithful is 20")
  }
}
Enter fullscreen mode Exit fullscreen mode

To access the methods inside the classes we first need to instantiate the class

class People {
  showName() {
    console.log("My name is Faithful")
  }
  showAge() {
    console.log("Faithful is 20")
  }
}

const people = new People()

people.showName()
people.showAge()
Enter fullscreen mode Exit fullscreen mode

We can see that My name is Faithful and Faithful is 20 gets logged on console.

if we want to make showAge() a private method inside the class People so outside the scope of the class it is not accessible
we just add the # sign in front of the showAge method like this #showAge

class People {
  showName() {
    console.log("My name is Faithful")
  }
  #showAge() {
    console.log("Faithful is 20")
  }
}

const people = new People()

people.showName()
people.showAge() 
Enter fullscreen mode Exit fullscreen mode

Alt Text
we can see the result on our console. An error is saying people.showAge is not a function. This is because #showAge() is now a private method inside the class People and can only be access via a public method in side the class People.

Now lets try and access the private method #showAge()

first we create new public method called showAll() inside the class People from this public method we can access the private method #showAge() and since our new method is a public one we would be able to print the age on the console. Take a look at the code below

class People {
  showName() {
    console.log("My name is Faithful");
  }
  #showAge() {
    console.log("Faithful is 20");
  }
  showAll() {
    this.showName()
    this.#showAge();
  }
}

const people = new People();
people.showAll();
Enter fullscreen mode Exit fullscreen mode

we can see that the error has disappeared and we can now access our private method via a public one showAll()
Alt Text

Private Accessors

Private accessors work very similar to private methods lets create a class named People and we have will have some methods

class People {
  get Name() {
    return "Faithful"
  }
  get Age() {
    return 20
  }
}

let people = new People();
console.log(people.Age);
Enter fullscreen mode Exit fullscreen mode

Alt Text
We can see 20 gets logged on console.

if we want to make Age a private accessor inside the class People so outside the scope of the class it is not accessible
we just add the # sign in front of the Age method like this #Age

class People {
  get Name() {
    return "Faithful"
  }
  get #Age() {
    return 20
  }

}

let people = new People();
console.log(people.Age) 
Enter fullscreen mode Exit fullscreen mode

Alt Text
we can see the result on our console is undefined.
Now lets try and access the private method #Age()

first we create new public method called publicAge() inside the class People from this public method we can access the private method #Age() and since our new method is a public one we would be able to print the age on the console. Take a look at the code below

class People {
  get Name() {
    return "Faithful"
  }
  get #Age() {
    return 20
  }
  get publicAge() {
    return this.#Age
  }
}

let people = new People();
console.log(people.publicAge)
Enter fullscreen mode Exit fullscreen mode

Alt Text
we can see that the error has disappeared and we can now access our private accessor via a public one publicAge()

Promise.any()

Promise.any() is like the opposite of Promise.all(). Promise.any() resolves if any of the supplied promised is resolved unlike promise.all() which waits for all promises to resolve before it resolves.
lets take a look at the example below
Basically we have 3 promises that resolves at random times. We have used setTimeout() function to set a time taken for each promise to resolve and we used Math.floor(Math.random) to give a random time to the setTimeout function so we really dont know which promise resolves first. This exaclty what happens in real world secenario.

const prom1 = new Promise((resolve, reject) => {
  setTimeout(
    () => resolve("this is the first promise"),
    Math.floor(Math.random() * 100)
  );
});
const prom2 = new Promise((resolve, reject) => {
  setTimeout(
    () => resolve("this is the second promise"),
    Math.floor(Math.random() * 100)
  );
});
const prom3 = new Promise((resolve, reject) => {
  setTimeout(
    () => resolve("this is the third promise"),
    Math.floor(Math.random() * 100)
  );
});

(async function() {
  const result = await Promise.any([prom1, prom2, prom3]);
  console.log(result); // Prints "A", "B" or "C"
})();
Enter fullscreen mode Exit fullscreen mode

Alt Text
from the result in console, we can see that our second promise resolves first.

Wait! what if none of the promises resolve? Well, Promise.any() throws an AggregateError exception. We will handle it in a try catch block.
Take a look at the example below

const rejected = new Promise((resolve, reject) => {
  setTimeout(
    () => reject("this is the first promise"),
    Math.floor(Math.random() * 100)
  );
});

try {
  (async function() {
    const result = await Promise.any([rejected]);
    console.log(result);
  })();
} catch(error) {
  console.log(error.errors);
}
Enter fullscreen mode Exit fullscreen mode

Numeric Separators

This feature enables developers to make their numeric literals more readable by creating a visual separation between groups of digits. Large numeric literals are difficult for the human eye to parse quickly, especially when there are long digit repetitions.

1000000000   // Is this a billion? a hundred millions? Ten millions?
101475938.38 // what scale is this? what power of 10?
Enter fullscreen mode Exit fullscreen mode

Examples

Regular Number Literals

let budget = 1_000_000_000_000;
// What is the value of `budget`? It's 1 trillion!
// 
// Let's confirm:
console.log(budget === 10 ** 12); // true
Enter fullscreen mode Exit fullscreen mode

Binary Literals

let nibbles = 0b1010_0001_1000_0101;
// Is bit 7 on? It sure is!
// 0b1010_0001_1000_0101
//          
// We can double check: 
console.log(!!(nibbles & (1 << 7))); // true
Enter fullscreen mode Exit fullscreen mode

Hex Literals

// Messages are sent as 24 bit values, but should be 
// treated as 3 distinct bytes:
let message = 0xA0_B0_C0;

// What's the value of the upper most byte? It's A0, or 160.
// We can confirm that:
let a = (message >> 16) & 0xFF; 
console.log(a.toString(16), a); // a0, 160

// What's the value of the middle byte? It's B0, or 176.
// Let's just make sure...
let b = (message >> 8) & 0xFF;
console.log(b.toString(16), b); // b0, 176

// What's the value of the lower most byte? It's C0, or 192.
// Again, let's prove that:
let c = message & 0xFF;
console.log(c.toString(16), b); // c0, 192
Enter fullscreen mode Exit fullscreen mode

lets view our result in console
Alt Text

Logical Assignment Operator

Logical Assignment Operator with &&

Logical assignment operator combines the logical operations(&&, || or ??) with assignment.

var x = 1;
var y = 2;
x &&= y;
console.log(x); // 2
Enter fullscreen mode Exit fullscreen mode

Techincally here's what is actually going on

if(x) {
  x = y
}
Enter fullscreen mode Exit fullscreen mode

Since x is a truthy value, it is assigned with the value of y, ie 2.

Just like the way we did with &&, we can do with || and ??.

x &&= y;
x ||= y;
x ??= y;
Enter fullscreen mode Exit fullscreen mode

Logical assignment operator with ||

var x = 1;
var y = 2;
x ||= y;
console.log(x);
Enter fullscreen mode Exit fullscreen mode

That means, the assignment operation happens only if x is a falsy value. In our code, x contains 1 which is a truthy value and hence, assignment does not happen. That is why our code prints 1 in the console.

Logical assignment operator with ??

?? is Nullish Coalescing operator in JavaScript. It specifically checks if a value is null or undefined.

var a;
var b = a ?? 5;
console.log(b);
Enter fullscreen mode Exit fullscreen mode

In line 2, if the value of a is null or undefined, the right hand side of ?? is evaluated and assigned to b.

Let us now consider ?? along with =.

var x;
var y = 2;
x ??= y;
console.log(x); // 
Enter fullscreen mode Exit fullscreen mode

Here the value of x is undefined. So the right hand side expression is evaluated and sets x to 2.

You can edit and test the code here

Happy Coding!

Top comments (15)

Collapse
 
nicozerpa profile image
Nico Zerpa (he/him)

Great post, Faithful!
The numeric separators are extremely handy, love them. I'll be using private methods a lot.

By the way, if someone is asking why JavaScript uses the # symbol for its private properties and not the private keyword, in this comment from the ECMA committee they explain why.

Collapse
 
faithfulojebiyi profile image
Faithful Ojebiyi

Thanks for pointing that out I forgot to include that in this post.

Collapse
 
tarektouati profile image
Tarek Touati

Great Post !

Can you please explain the difference between
Promise.race and Promise.any ?
They booth act the same ? Does Promise.race have been deprecated ?

Collapse
 
faithfulojebiyi profile image
Faithful Ojebiyi

They are a bit different
Promise.race is resolved as soon as any of the promises you feed it resolves, whether they are fulfilled or rejected. Promise.any is settled as soon as any of the promises you feed it is fulfilled or all the promises are rejected. If all promises are rejected it returns an AggregateError.

So Promise.race returns a promise that is settled regardless it being resolved or rejected. But promise.any will not return if one promise is rejected. It will continue until the first resolved promise. If in any case none of the promises resolves then it returns an AggregateError.
I hope I cleared your confusion

Collapse
 
faithfulojebiyi profile image
Faithful Ojebiyi

Promise.race() isn't deprecated either.. They have different use cases

Collapse
 
martijn_scheffer_f5485f4b profile image
Martijn Scheffer • Edited

WHEN does Promise.any throw an error ? how can it know that none of the promise will be resolved later ?

did you mean that when ALL promises are rejected an exception is thrown ? that isn't exactly the same

Collapse
 
faithfulojebiyi profile image
Faithful Ojebiyi

When you use Promise.any, your literally saying which ever promise resolves first. It would only throw an error if non resolves.

If you want to make sure all your promise resolves or you want to catch error when any of the promises is rejected you should use Promise.all instead

Collapse
 
pawandeore profile image
pawan deore

Nice Post

Collapse
 
consciousness_dev profile image
Ario Setiawan

Finally Promise.any solve my error handling easily! ( 0 )

Collapse
 
xexnon profile image
Xexnon

Seems like I'll be using the replace all method frequently, and the numeric separators will come in really handy... A detailed, well written, explanatory post. Keep it up!

Collapse
 
faithfulojebiyi profile image
Faithful Ojebiyi

Very true the replaceAll method and the numeric separators were really needed.
I see myself using the private methods and accessors alot

Collapse
 
abulka profile image
Andy Bulka

Running the code above gives me Invalid character: '#'

Collapse
 
faithfulojebiyi profile image
Faithful Ojebiyi

Update your chrome browser if thats what you are using.

Collapse
 
mkorkut profile image
Korkut

it was a great article thanks

Collapse
 
radix018 profile image
Rahul

Quite informative. I got to learn about the new operators, kinda tricky though, the Promise.all also looks very promising.