What’s New Features in ECMAScript 2021 (ES2021/ES12)

david medragh
6 min readJan 31, 2021

--

ECMAScript 2021 version is expected to be released in the month of June 2021. The list prepared based on ECMAScript proposal status and the new features released by Google Chrome V8 engine.

The features listed below, at the time of writing 2021–01–31 are supported in Google Chrome Canary build.

The string replaceAll() Method

String.prototype.replaceAll("source String","replacement String") replaces all occurrence of the source Stringstring with replacement Stringstring value.

Currently JavaScript string has only a replace() method. It can be used to replace a string with another string.

const str = "david.medragh works on  software dev at company softwareDev";/* The replace method here will replace only the first occurence of "software" by "WebSite" */
const newStr = str.replace("software", "WebSite");
console.log(newStr); // "david.medragh works on WebSite dev at company softwareDev"

As you could see the input pattern is a string, replace() method only replaces the first occurrence. That is why in the code, the second occurrence of “software” is not replaced.

If we want a full replacement we must replace the pattern as a with regular expression.

const str = "david.medragh works on  software dev at company softwareDev";
const newStr = str.replace(/software/g, "WebSite");
console.log(newStr); // "david.medragh works on WebSite dev at company WebSiteDev"

String.prototype.replaceAll() willbring the full replacement option even when the input pattern is a string.

Private Methods

Private methods will be accessible only inside the class where it is defined. to define a private method names must starts with #.

class Person {  // Private method
#setType() {
console.log("I am Private");
}
// Public method
show() {
this.#setType();
}
}const personObj = new Person();
personObj.show(); // "I am Private";
personObj.setType(); // TypeError: personObj.setType is not a function

Since setType() is a private method, personObj.setType returns undefined. Trying to use undefined as a function throws TypeError.

Private Accessors

Accessor functions can be made private by prepending # to the function name.

class Developer{
// Public accessor
get name() { return "david.medragh" }
set name(value) {}
// Private accessor
get #age() { return 100}
set #age(value) {}
}

In the above code get and set keywords make name an accessor property. Even though name looks like a function, it can be read like a normal property.

const obj = new Developer();
console.log(obj.name); // "david.medragh"
console.log(obj.age); // undefined

WeakRef and Finalizers

WeakRef mean Weak References.

Generally we use weak references to implement caches or mappings to large objects.
It let us to not keep a lot of memory for a long time . We can allow the memory to be garbage collected soon and later if we need it again, we can generate a fresh cache.

JavaScript is a garbage collected language. If a variable is no longer reachable, JavaScript garbage collector automatically removes it. You can read more on JavaScript garbage collection here in MDN site.

Let consider the following code:

const callback = () => {
const aBigMemoryConsumerObj = {
name: "I am a big object"
};
console.log(aBigMemoryConsumerObj);
}
(async function(){
await new Promise((resolve) => {
setTimeout(() => {
callback();
resolve();
}, 2000);
});
})();

This code create a function named callback() and execute it using setTimeout(). it will execute asynchronous code in synchronous way.

When executing above code, it prints "I am a big object" after 2 seconds. Based on how we use the callback() function,
aBigMemoryConsumerObjis stored in memory forever, may be.

Let us transformaBigMemoryConsumerObjas a weak reference.

const callback = () => {
const aBigMemoryConsumerObj= new WeakRef({ name: "I am a big object" }); console.log(aBigMemoryConsumerObj.deref().name);}
(async function(){
await new Promise((resolve) => {
setTimeout(() => {
callback(); // Guaranteed to print "I am a big object"
resolve();
}, 2000);
});
await new Promise((resolve) => {
setTimeout(() => {
callback(); // No Gaurantee that "I am a big object" is printed
resolve();
}, 5000);
});
})();

A WeakRef is created using new WeakRef(). Later the reference is read using .deref() method. Inside the async function, The first setTimeout() will surely print the value of name. That is guaranteed in the first turn of event loop after creating the weak reference.

But there is no guarantee that the second setTimeout() prints "I am a big object". It might have been sweeped by the gargage collector. Since the garbage collection works differently in different browsers, we cannot guarantee the output. That is also why, we use WeakRef in situations like managing cache.

Finalizers

FinalizationRegistry is a companion feature of WeakRef. It lets us register callbacks to be invoked after an object is garbage collected.

const registry = new FinalizationRegistry((value) => {
console.log(value);
});

Here registry is an instance of FinalizationRegistry. The callback function passed to FinalizationRegistry gets triggered when an object is garbage collected.

(function () {
const obj = {};
registry.register(obj, "I am a big object");
})();

The attaches obj to registry. When obj is garbage collected, the second argument of .register() method is passed to the callback function. So, according to our code logic, when obj is garbage collected, "I am a big object" is passed to the callback function and is printed in the console.

When executed above code in Google Chrome Canary console, after about 1 min, it printed "I am a big object" in the console. Another way to force garbage collection in chrome is to click on Collect Garbage icon. We can find it in Performance tab.

Promise.any() and AggregateError

Promise.any() resolves if any of the supplied promises is resolved. Below we have 3 promises, which resolves at random times.

const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("A"), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("B"), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve("C"), Math.floor(Math.random() * 1000));
});

Out of p1, p2 and p3, whichever resolves first is taken by Promise.any().

(async function() {
const result = await Promise.any([p1, p2, p3]);
console.log(result); // Prints "A", "B" or "C"
})();

What if none of the promises resolve? In that case Promise.any() throws an AggregateError exception. We need to catch it and handle it.

const p = new Promise((resolve, reject) => reject());try {
(async function() {
const result = await Promise.any([p]);
console.log(result);
})();
} catch(error) {
console.log(error.errors);
}

For demo purpose, only one promise is passed to Promise.any(). And that promise is rejected. The above code logs following error in console.

Logical Assignment Operator With &&, || and ??

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

x &&= y;
x ||= y;
x ??= y;

Logical assignment operator with &&

var x = 1;
var y = 2;
x &&= y;
console.log(x); // 2

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

Logical assignment operator with ||

Here is the code.

var x = 1;
var y = 2;
x ||= y;
console.log(x); // 1

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

Logical assignment operator with ??

Poosible on nodejs ++15

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

Here is the code.

var x = 1;
var y = 2;
x ??= y;
/*Here if the value of x is null or undefined. So the right hand side expression is evaluated and sets x to 2.*
console.log(x); // 1

Since x is not null or undefined value, it is assigned with the value of x, ie 1.

--

--