What’s new in ES2021?

Rahul
5 min readMar 31, 2021

--

ECMAScript 2021 version is expected to be released this year. This version doesn’t include as many new features as those that appeared in ES6 (2015). However, some useful features have been incorporated.

Here are some new features that you can expect from the ECMAScript proposal status from TC39:

String.protype.replaceAll

The String.prototype.replaceAll() method allows you to replace all occurrences of a substring with another string that you defined. Currently, the .replace() method only replace the first occurrence of the substring while ignoring the rest:

const queryString = 'q=query+string+parameters';
const withSpaces = queryString.replace('+', ' ');
console.log(withSpaces); // q=query string+parameters

The only way to replace all occurrences is to use .replace() with a global regular expression as follows:

// replace all '+' with a 'space'
const queryString = 'q=query+string+parameters';
const withSpaces = queryString.replace(/\+/g, ' ');
console.log(withSpaces); // q=query string parameters

With the replaceAll() method, you don’t have to use a regular expression anymore:

const queryString = 'q=query+string+parameters';
const withSpaces = queryString.replaceAll('+', ' ');

Promise.any

Promise.any() method takes in a list of promises and returns a value, as soon as it hits the first resolved promise from the list. If all the promises are rejected then it will throw an AggregatedError message.

Promise.any(promises).then(
(first) => {
// Any of the promises was fulfilled.
},
(error) => {
// All of the promises were rejected.
}
);

This method is different from Promise.race because with race, the promise short-circuits once when one of the given promises resolves or rejects, but here it resolves if any of the promises are fulfilled and it is rejected only if all the promises are rejected.

Here’s an example demonstrating the same:

Promise.any([new Promise((resolve, reject) => setTimeout(reject, 200, 'One')),new Promise((resolve, reject) => setTimeout(resolve, 1000, 'Two')),new Promise((resolve, reject) => setTimeout(resolve, 2000, 'Three'))]).then((value) => console.log(`Result - ${value}`)).catch((err) => console.log(err));
// OUTPUT
// Result - Two

WeakRefs

WeakRefs are useful in many scenarios. For example, we can use a Map object to implement a cache with many keys-values that need a lot of memory. In this case, what is convenient for us is to free the memory that the key-values pairs occupy as soon as possible, and WeakRefs allow us to do this.

A WeakRef object contains a weak reference to an object. A weak reference to an object is a reference that does not prevent the object from being recovered by the garbage collector. On the other hand, a standard reference keeps the object in memory.

References to objects in JavaScript are held. That is, as long you have a reference to an object, it won’t be garbage-collected.

Example:

const obj1= { a: 10, b: 20};

Currently, we have WeakMap() and WeakSet() that use WeakRefs:

WeakMap object is a collection of key-value pairs in which the keys are weakly referenced.

Let’s consider the following example:

const myObj = {...};

A function that uses the previous object:

function useTheObj(obj){
doSomethingWith(obj);
}

I want to keep track of how many times the method was called with a particular object and report if it happens more than 1000 times:

var map = new Map();function useTheObj(obj){ doSomethingWith(obj); //get the number of called times 
//or initialize to 0 is null.
var called = map.get(obj) || 0; //Increase the counter in one
called++; if(called > 1000) {
console.log(’called more than 1000 times’);
}; map.set(obj, called);
}

This solution works, but it has a memory leak because every object passed to the function keeps on the map forever, and it isn’t garbage collected. The solution is to use a WeakMap:

var wmap = new WeakMap();function useTheObj(obj){ doSomethingWith(obj); //get the number of called times
//or initialize to 0 is null.
var called = wmap.get(obj) || 0; //Increase the counter in one
called++; if(called > 1000) {
console.log(’called more than 1000 times’);
}; wmap.set(obj, called);
}

Logical assignment operators

Logical assignment operator (by Justin Ridgewell and Hemanth HM) proposal combines Logical Operators(&&, ||, ??) and Assignment Expressions:

Until now, JavaScript has the following assignment operators:

=
Assignment operator./=
Division assignment.*=
Multiplication assignment.&&=
Logical AND assignment.||=
Logical OR assignment.??=
Logical nullish assignment.**=
Exponentiation assignment.%=
Remainder assignment.+=
Addition assignment.-=
Subtraction assignment.<<=
Left shift assignment.>>=
Right shift assignment.>>>=
Unsigned right shift assignment.&=
Bitwise AND assignment.^=
Bitwise XOR assignment.|=
Bitwise OR assignment.Destructuring assignment operators:[a, b] = [10, 20]
{a, b} = {a:10, b:20}

And with this proposal, we would combine Logical Operators and Assignment Expressions:

a ||= b
//Equivalent to : a || (a = b), only assigns if a is Falsy.a &&= b
//Equivalent to: a && (a = b), only assigns if a is Truthy.a ??= b
//Equivalent to: a ?? (a = b), only assigns if a is Nullish.

Numeric separators

Numeric separators (Christophe Porteneuve’s) extend the existing NumericLiteral to allow a separator character between digits.

Thanks to this feature are easy to make numeric literals more readable by creating a visual separation between groups of digits.

For example:

const money = 1000000000000;

The above numeric literal is challenging to read, but we can use underscores as separators to make it easier to read:

const money = 1_000_000_000_000;

Ok, now it’s easier to read the “money” variable.

Numeric separators can be used in different positions:

const money = 1_000_000.123_456;

Also, numeric separators are available in the Octal integer literals.

const octal = 0o123_123;

dateStyle and timeStyle options for Intl.DateTimeFormat

Now, we can use dateStyle and timeStyle to request a locale-specific date and time of a given length. We just need to pass them as options and the rest is handled.

// short
new Intl.DateTimeFormat("en" , {
timeStyle: "short"
}).format(Date.now())
// "6:35 PM"
// medium
new Intl.DateTimeFormat("en" , {
timeStyle: "medium"
}).format(Date.now())
// "6:35:13 PM"
// long
new Intl.DateTimeFormat("en" , {
timeStyle: "long"
}).format(Date.now())
// "6:35:55 PM GMT+5:30"

And for dateStyle here are a few examples:

// short
new Intl.DateTimeFormat("en" , {
dateStyle: "short"
}).format(Date.now())
// "11/19/20"
// medium
new Intl.DateTimeFormat("en" , {
dateStyle: "medium"
}).format(Date.now())
// "Nov 19, 2020"
// long
new Intl.DateTimeFormat("en" , {
dateStyle: "long"
}).format(Date.now())
// "November 19, 2020"

Conclusion

That will be all of the features expected to be implemented in ES2021. As a developer, it is important to stay up-to-date with the new features of any programming language. I hope, I have given you a brief idea of what are all yet to come. 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 only with tricks or a lot of verbosity. Thank you for reading.

Happy coding!

--

--