New JavaScript Features You Can Expect in ES2021

Nathan Sebhastian
Bits and Pieces
Published in
5 min readFeb 16, 2021

--

ES2021 or ES12 is scheduled for release sometime in June 2021. Here are some new features that you can expect from the ECMAScript proposal status from TC39:

  • String.prototype.replaceAll
  • Promise.any
  • WeakRefs and finalizer
  • Logical Assignment Operators
  • Numeric separator

This post will help you to understand the use of each new feature, starting from the first one.

The String replaceAll() method

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('+', ' ');

The Promise.any() method

The Promise.any() method returns a promise that will resolve as soon as one of the promises are resolved. If all of the promises are rejected, the method will throw an AggregateError exception holding the rejection reasons.

Here’s an example:

const promiseOne = new Promise((resolve, reject) => {
setTimeout(() => resolve("one"), 3000);
});
const promiseTwo = new Promise((resolve, reject) => {
setTimeout(() => resolve("two"), 2000);
});
const promiseThree = new Promise((resolve, reject) => {
setTimeout(() => resolve("three"), 1000);
});
Promise.any([promiseOne, promiseTwo, promiseThree]).then(
(first) => {
// Any of the promises was fulfilled.
console.log(first); // three
},
(error) => {
// handling error here
}
);

If all of the promises given are rejected, the AggregateError exception will be thrown.

Here’s another example showing the exception using the async/await syntax:

const promiseOne = new Promise((resolve, reject) => {
setTimeout(() => reject(), 1000);
});
try {
const first = await Promise.any([promiseOne]);
// Any of the promises was fulfilled.
} catch (error) {
console.log(error);
// AggregateError: All promises were rejected
}

WeakRefs for objects

The WeakRef, which stands for Weak References, allows you to create a weak reference to an object. The primary use of Weak Reference is to implement caches or mappings of a large object.

A regular/ strong JavaScript object reference will never be claimed by the garbage collector. A weak reference to an object, in contrast, can be claimed by JavaScript garbage collector:

const normalObj = {name: "John"};
const refObj = new WeakRef({name: "John"});

When you need to read the value of WeakRefs , you need to use the deref() method to return the instance’s target object:

const refObj = new WeakRef({name: "John"});
const obj = refObj.deref();
console.log(obj.name); // John

Because the implementation detail of when, how, and whether JavaScript garbace collection actually occurs or not is up to the JavaScript engine implementation, you may observe different behavior between one JavaScript environment and another.

The correct use of WeakRef takes careful thought, and it’s best to avoid implementing one if possible. Unless you’re a JavaScript library maintainer, you will most likely never need to use it.

Finalizers

The Finalizer is a companion feature of WeakRef that allows you to execute a piece of code after an object has become unreachable to the program.

In short, you can register a callback function that gets triggered after the garbage collection occurs. You can create a registry by passing the callback to the FinalizationRegistry object:

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

Then, you can register any object you want to cleanup for by calling the register() method, passing both the object and the value you want to pass to the callback function:

registry.register({name: "John"}, "any value");

The object passed into the register() method will be weak-referenced so that it can be garbage collected. Based on the code above, the callback will log “any value” to the console.

Both WeakRefs and Finalizers are tricky concepts. You can see the weak reference proposal to learn more.

Logical assignment operator

The logical assignment operator combines Logical Operators and Assignment Expressions, allowing you to write a shorter syntax for variable value checking.

For example, the following code checks whether the value of x is falsy and only assign a new value when it is:

let x;
if(!x){
x = 7;
}

Using the logical assignment operator, you can replace the above code with the following:

let x;x ||= 7; // since x is undefined, it's assigned the number 7
console.log(x); // 7

The logical assignment works with logical AND (&&) and nullish coalescing operator (??) as well:

let x = null;x ??= 7 // assign 7 to x when it's null or undefinedlet y = "Hello";y &&= 9 // assign 9 to y when it's value is truthy

Numeric separator

The numeric separator proposal helps you to write more readable code by allowing you to use underscore (_) as a separator when you define a numeric value.

The following code shows the comparison between a normal number and a separated number for one million:

const aMillion = 1000000;
const separatedMillion = 1_000_000;

As you can see, separating the number with an underscore makes it much more readable. You can even use it on numbers with floating points as follows:

const randomFloat = 4.7_857_123;

The separator won’t be included when you use the value for operations:

const randomFloat = 4.7_857_123;
console.log(randomFloat); // 4.7857123

Keep in mind that you can only use the separator between two digits:

const num = 4_7; // 47// All the above throws an error
const a = 47_;
const b = _47;
const c= 3._47;

Conclusion

That will be all of the features expected to be implemented in ES2021. While it doesn't have many new features, these confirmed updates certainly can help you to write better JavaScript code.

Build anything from independent components

Say goodbye to monolithic applications and leave behind you the tears of their development.

The future is components; modular software that is faster, more scalable, and simpler to build together. OSS Tools like Bit offer a great developer experience for building independent components and composing applications. Many teams start by building their Design Systems or Micro Frontends, through shared components. Give it a try →

An independently source-controlled and shared “card” component. On the right => its dependency graph, auto-generated by Bit.

--

--

Web Developer and Writer. Sharing what I learn on productivity and success.