What's new in ES2021 or ES12?

📖 6 min read

As I explain in my Modern JS with ES6+ post, the TC39 is the group that is responsible for advancing the ECMAScript specifications and standardizing these specifications for the JavaScript language. Ever since the 2015 release of ES2015 or ES6, we've had a yearly release cycle for the next version of ECMAScript. Before a feature can be added to a release, it must first go through four proposal stages before finally being approved for release. Here we'll be looking at the features that were added in ES2021 or ES12.

You can check out all of the finished proposals, from across the years, on GitHub.

String.prototype.replaceAll

Syntax
js
replaceAll(regExp, replacementString);
replaceAll(regExp, replacerFunction);
replaceAll(substr, replacementString)
replaceAll(substr, replacerFunction)

The .replaceAll() method is very similar to the .replace() method, but the latter will replace only the first instance and the former replaces all occurrences of a regular express or substring. This method can accept either a substring or a regular express for the first parameter and either the replacement string or a replacer function that returns a string for the second parameter. It's important to note that if the first argument of a replaceAll() call is a RegExp object or regular expression literal, and the second parameter is a function, the function will be invoked multiple times. For more information, like what parameters a replacerFunction has passed into it, check out the MDN docs.

Examples
js
const str = 'James lives in Central Florida. James has a dog named d00d.';
const strReplaced = str.replace( 'James', 'Kevin' ); // Kevin lives in Central Florida. James has a dog named d00d.
const strAllReplaced = str.replaceAll( 'James', 'Kevin' ); // Kevin lives in Central Florida. Kevin has a dog named d00d.

Promise.any

Syntax
js
Promise.any(iterable);

The Promise.any() method accepts an iterable of Promise objects as it's only parameter and will return a single Promise objet that resolves when any of the passed in Promise objects resolve, with the value of the resolved original Promise object.

Examples
js
const promise1 = new Promise( resolve => setTimeout( resolve, 100, 'first' ) );
const promise2 = new Promise( resolve => setTimeout( resolve, 300, 'second' ) );
const promise3 = new Promise( resolve => setTimeout( resolve, 700, 'third' ) );
Promise.any( [
promise1,
promise2,
promise3,
] ).then( value => console.log( value ) );

WeakRefs

WeakRefs allow you to create a reference to an object while not preventing garbage collection from removing it later. References to objects are strongly held in JavaScript, which means that as long you have a reference to the object, it won’t be garbage-collected. WeakRefs are a way of creating a reference that is not strongly held and can be garbage-collected.

According to the proposal itself, the correct use of WeakRef and FinalizationRegistry take careful thought, and they are best avoided, if possible. This is because they rely heavily on the garbage collection that takes place in the language. As a brief and nowhere near expansive explanation of it, JavaScript is a garbage collected language which means that if a variable is no longer reachable, the garbage collector will automatically remove it from memory. The hard part here is that the when and how garbage collection takes place is entirely up to the JavaScript engine itself. The different engines will more than likely produce different results on when or how the garbage collection takes place.

The concept is pretty simple but the use case is very rare, specific, and advanced and since these are power-user features, most usage will take place within frameworks or libraries.

A WeakRef is created with the new WeakRef constructor, and the value of the WeakRef variable can be accessed via the deRef method.

Examples
js
const wr = new WeakRef( {
name: 'Kevin'
} );
console.log( wr.deref()?.name );
//output: Kevin

You can force clear the garbage collector in Chrome within the Performance tab of the dev tools.

Force JavaScript garbage collection in Chrome

Logical Assignment Operators

The purpose of this proposal was essentially to combine logical operators (&&, || or ??) and assignment expressions.

Logical OR assignment operator ||=

Syntax
js
x ||= y;

The logical OR assignment operator, ||=, is a short-circuit evaluation just like the logical OR operator (||), which means that the second operand is only evaluated if the first operand is evaluated as falsy. The syntax expression above is the same as x || (x = y) and not x = x || y, where the latter always performs the assignment. This means that y will only be assigned to x if x is falsy, otherwise, x retains its original value.

Examples
js
const updateVoteCount = user => {
// We can do this.
if ( ! user.votes ) {
user.votes = 1
}
// Or this.
user.votes = user.votes || 1
// Or use the new logical assignment operator.
user.votes ||= 1
}

Logical AND assignment operator &&=

Syntax
js
x &&= y;

The logical AND assignment operator, &&= is also a short-circuit evaluation just like the logical AND operator (&&), which means that the second operand is only evaluated if the first operand is evaluated as truthy. The syntax expression above is the same as x && (x = y) and not x = x && y, where the latter always performs the assignment. This means that y will only be assigned to x if x evaluates as truthy, otherwise, x retains its original falsy value.

Examples
js
let x = 1;
const y = 2;
x &&= y;
console.log( x ); // 2

Logical nullish assignment operator ??=

Syntax
js
x ??= y;

The logical nullish assignment operator, ??= is also a short-circuit evaluation just like the logical nullish operator (??), which means that the second operand is only evaluated if the first operand is evaluated as null or undefined. The syntax expression above is the same as x ?? (x = y ) and not x = x ?? y, where the latter always performs the assignment. This means that y will only be assigned to x if x evaluates as null or undefined, otherwise, x retains it's original value.

Example
js
const getKey = () => 'test-key';
const user = { name: 'Kevin', age: 33 };
user.key ??= getKey();
console.log( user.key ); // 'test-key'

Numeric separators

Is 1000000000 a million, a billion, or somewhere in between? With the new numeric separator you can more easily determine the actual value of Numbers and BigInts by allowing the numbers to be separated by _ to separate number groups.

Examples
js
let num = 1_000_000_000; /// 1 billion.
num = 1_000_000; // 1 millon.
num = 100_000_000; // 100 millon.