Published on

Whats new in ES2021


Since I am personally always curious what new features ECMAScript brings with it, I thought I can write a small post that should serve as an overview. Important to be able to really understand individual features especially weakRefs, you have to work through the individual linked proposals. Furthermore, it is a good idea to take a look at MDN Web Docs.

List of new Features

Logical Assignment Operators

For less code and more readability the logical assignment operator was inspired by Rubys Abbreviated Assignment

a ||= 0
a &&= 1
p a # prints 1

In Javascript we will basically get three new assignment operators.

  • &&=
  • ||=
  • ??=

Logical OR assignment (||=)

JS will only assign a new value to the variable, if the old value is falsly (false, 0, -0, null, undefined, '', ...)


let x = undefined;
// x is undefined so js will assign foo to it
x ||= 'foo';
// x is truthy js will not assign bla to it
x ||= 'bla';

Logical and assignment (&&=)

JS will only assign a new value to the variable, if the old value is truthy ( all values that are not falsly)


let x = undefined;

// x is undefined so js will not assign foo to it
x &&= 'foo';

Logical nullish assignment (??=)

The nullish operator (??) was introduced with ES2020. Unlike falsely values, nullish just means null or undefined. Other than that the rest of the logic is the same than for falsley values. JS will only assign a new value to the variable, if the old value is nullish.


To better understand the difference between ||= and ??=. What will be x and will be y in the example below:


let x = '';
let y = '';

x ??= 'foo';
y ||= 'foo';


x will still be a empty string, and y will be foo since a empty string is falsely value

Important be aware of short circuit evaluation

Summary It is important to understand that for all new logical assignment operators, the js compiler uses the short circut method. This means for example for the new logical nullish operator that if a value on the left is not nullish, values on the right are not executed. This has advantages especially for functions that may have side effects.

So x ??= y is not the same as x = x ?? y x ?? (x = y) would be the more equivalent description

Numeric Separators

Numeric Separators are a nice feature for us humans to read numbers better. With an underscore we can press in large numbers better. This also works for binary numbers or hex numbers.


const decimalValue = 666;
const octalValue  = 0o12_32;
const hexValue    = 0x02_9A;
const binaryValue = 0b0010_1001_1010;


The new native function in javascript string.replaceAll will replace the old hacky method where you had to use a regex to change all chars in a string


const string = 'Ring-ding-ding-ding-dingeringeding';
const withSpacesOld = string.replace(/\-/g, ' ');
const withSpacesNew = string.replaceAll('-', ' ')

Promise.any and AggregateError

With ES2015, the first two new Promise combinators were introduced. Promise.race and Promise.all. ES2020 introduced Promise.allSettled.

In ES2021, Promise.any is now added. In a simplified way you can imagine that in a block of asynchronous api calls, you are pleased and can continue to work if any of them comes back successful. If none of the api calls in the block returns, the whole block fails. If all of them fail, a new error type is also thrown, namely Aggregate Error.


const onlyRejectedPromises = [
  Promise.reject("ERROR everything is a mess"),
  Promise.reject("ERROR bla"),

const onlyResolvedPromises = [
  new Promise((resolve) => {
    setTimeout(resolve, 100, "Not to fast");
  new Promise((resolve) => {
    setTimeout(resolve, 50, "Faster than light");

Promise.any([...onlyResolvedPromises, ...onlyRejectedPromises])
  .then((value) => {
    // faster than light will be printed
  .catch((aggregateError) => {
    // will not go into the catch
    // at last one promise was successful

  .then((value) => {
    // will not go into the then
  .catch((aggregateError) => {
    // will go into the catch
    // ['ERROR everything is a mess', 'ERROR bla']

WeakRefs and FinlizationRegistry

For weakRefs you would probably need a separate blog, because this new feature is very complex and you need to understand exactly how the garbage collector works in Javascript. Therefore I linked a good video which helped me to understand this concept better. In the following I try to explain this new feature with my own words.

Weak JavaScript - HTTP 203

Garbage Collector

To understand weak refs, it is important to first understand how Javascript frees up space when a program has too much memory. In low level languages like C, the developer has to ensure that a variable that is no longer needed is also wasted from memory. In JavaScript this is done automatically by the garbage collector. A big problem for the garbage collector in Javascript is to know if an object which has a reference to the memory is really needed. Here weakRefs can help.


A classic use case when it comes to Javascript is of course to store the value of a DOM element into a new variable at runtime. This variable would classically have a strong reference to the dom element. This means in reverse that the garbage collector would never remove the object with the reference from memory. If you just use weakRefs the GC knows that it also has to remove the variable.


The following code shows a code example on a use case for weak reference. source

class Counter {
  constructor(element) {
    // Remember a weak reference to the DOM element
    this.ref = new WeakRef(element);

  start() {
    if (this.timer) {

    this.count = 0;

    const tick = () => {
      // Get the element from the weak reference, if it still exists
      const element = this.ref.deref();
      if (element) {
        element.textContent = ++this.count;
      } else {
        // The element doesn't exist anymore
        console.log("The element is gone.");
        this.ref = null;

    this.timer = setInterval(tick, 1000);

  stop() {
    if (this.timer) {
      this.timer = 0;

const counter = new Counter(document.getElementById("counter"));
setTimeout(() => {
}, 5000);


The FinalizationRegistry object lets you request a callback when an object is garbage-collected.

Example source

const sleep = (ms) => new Promise(r => setTimeout(r, ms));

let waitingForCleanup = true;
const registry = new FinalizationRegistry((heldValue) => {
  console.log(`cleanup: ${heldValue}`);
  waitingForCleanup = false;

let foo = {};
registry.register(foo, 42);
foo = undefined; // Clear strong reference

Important in general weakRefs and FinalizationRegistry should be avoided since GC is not deterministic and you never know if the garbage collector will ever remove the object from memory. So if your code needs optimisation that is depended on weak refs, you should not use it.


Let's now briefly summarise the new features. In total there are five new features, three of them I would call syntax sugar (logical assignment, string.prototype.replaceAll(), numeric separators). Promise.any is a continuation and gives us developers more possibilities to display Promise combinations. With weakRefs the normal developer will probably have less contact in his daily use. WeakRefs will surely be a good solution for certain optimisation problems. At the end of the day, they give the developer more tools to help the garbage collector free up even more unneeded memory.