New Features in ES2021
Tags: javascript, node, webdev, 100daysofcode
Published: 2020-10-27 10:34:12

In this post, we'll take a look at the most interesting features that _should_ be included in _ES12_, the new version of the standard.

> The new feature that will be included in _ES12_, aka _ES2021_, the JavaScript specification developed by ECMA for 2021, are currently under discussion, and a few weeks ago, on October 15th, a new [draft](https://tc39.es/ecma262/) was released on TC39 website.
You can also check active proposals either [here](https://tc39.es) or on [GitHub](https://github.com/tc39/proposals).

# `String.prototype.replaceAll`

This is a convenience method that addresses a specific lack in `String.prototype.replace`.

Now you can easily replace all occurrences of a given string.

```javascript
const str = "With great power comes great responsibility";

const str1 = str.replaceAll("great", "little");     // "With little power comes little responsibility"
```

Notice that both these methods return a new string, without affecting the original variable.

Before `replaceAll` was introduced, if you had to perform string-replacement you could already use the `replace` method. Things are fine with `replace`, unless you need to replace all the occurrences of a pattern with a new string.

For instance:
```javascript
const str2 = str.replace("great", "little");     // "With little power comes great responsibility"
```

That's probably not exactly what you meant to say, right?
There is already an alternative way to use `replace` method and achieve what we wanted: we need to use regular expressions.

```javascript
const str3 = str.replace(/great/g, "no");     // "With no power comes no responsibility"
```

Better this time. The catch is, you need to be careful and add the 'g' flag to your `RegExp` in order to make it a *g*lobal replacement. This means it's error prone, of course, because it's easy to forget it, and you won't notice unless you test your code with a string that needs global replacement.

Is that all? Well, to be honest, there isn't a single catch. Regular expressions are slower, but even worse, sometimes you might not know your pattern in advance:

```javascript
function fillTemplateVar(template, tag, value) {
    return template.replace(tag, value);
}
```

The example above shows a typical situation where you would like to replace a pattern in some kind of template, something like `<h1>{title}<h1>`, were you you'd like to substitute the template-variable title with an actual title: `fillTemplateVar('<h1>{title}<h1>', /\{title\}/g, someValue)`.

But if you need to replace the tags dynamically, and you get them passed as strings, that function wouldn't work unless you use a workaround: 

```javascript
fillTemplateVar('<h1>{title}<h1>', new RegExp(tag, 'g'), someValue)
```

Using `replaceAll`, instead, allows you to avoid an unnecessary conversion, and use strings comparison instead of regular expressions matching.

This method is not yet supported in NodeJs, but most [browsers](https://caniuse.com/mdn-javascript_builtins_string_replaceall) already implemented it.

# `Promise.any`

Another method added to the language's toolbelt for handling promises. In ES2020 `Promise.allSettled` was added to run multiple promises in parallel and act when all of them were settled, either as fulfilled or as rejected.

This new method also takes an iterable (f.i. an array) of promises, but only resolves when either the first one of them is fulfilled, or all of the promises passed are rejected.

```javascript
Promise.any([get('www.google.com'), get('www.twitter.com')])
    .then(result => {
        console.log('First promise settled: ', result)
    });
```

So, you might have noticed that this method is quite similar to an existing one, `Promise.race`, right? But here is the thing with `race`: it will settle when any of the promises is _settled_, it doesn't matter if by being rejected or fulfilled. Hence, in those situations where you try multiple solutions and are happy with at least one working and fulfilling its promise, the promise created by `race` method wouldn't be helpful.

Let's see an example, building an image roulette:

```javascript
const p1 = new Promise((resolve, reject) => {
  reject("Rejected");
});

const p2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1500, "Resolved, but slowly");
});

const p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "Resolved quickly");
});

// This is resolved, and logs "Resolved quickly"
Promise.any([p1, p2, p3]).then((value) => {
  console.log("Promise.any -> ", value);
});

// This is rejected
Promise.race([p1, p2, p3]).then((value) => {
  console.log("Promise.race -> ", value);
});
```

There is one question left to discuss: what happens if none of the promises passed is fulfilled?
In that situation (which includes the case where the iterator passed to `any` is empty), the method throws an `AggregateError`,  a new type of exception contextually [introduced](https://caniuse.com/mdn-javascript_builtins_aggregateerror) in ES2021.

Support is still in development, only some [browsers](https://caniuse.com/mdn-javascript_builtins_promise_any) already implemented it, and it's not in NodeJs yet.

# Numeric Separators

This is a _cosmetic_ change that might have a low impact on the performance or cleanness of your JavaScript code, but it might help avoiding errors whenever you need to insert numeric values to your code (f.i. while defining constants).
Numeric Separators will make it easier to read these numeric values you define, by letting you use the underscore character `_` as a separator between groups of digits. 

You can use however many separators you like, and the groups of digits may be of any size - the only limitations are that you can't have two adjacent separators nor you can place them at either end of the number. A few examples will clarify:

```javascript
const MILLION = 1_000_000;       // 1000000
const BILLION = 1_000_000_000;   // 1000000000

// You can break the digits in any way
const WHATEVER = 1234_5678_9_0;  // 1234567890

// And that's not limited to integers!
const PI = 3.1415_9265_3589;     // 3.141592653589

// Now, do not try this at home! 😁

const BAD_PI = 3.14_15_;          // SyntaxError
const NO_MILLION = _1_000_000;    // ReferenceError! 😱 
      // Remember that variable names can start with underscore... 😉
```

I don't know how much I'm going to use this feature, but the good news is that it is already supported in most [browsers](https://caniuse.com/mdn-javascript_grammar_numeric_separators) and in NodeJs since version 12.5.0, so we already have a choice.

# `Intl.ListFormat`

Before we delve into this new feature, let's take a step back: the `Intl` object is the namespace for the **_ECMAScript_ Internationalization API**, which provides a bunch of helper methods to support internalization efforts, like language-sensitive string comparison, number formatting, and date and time formatting.

In this case, the new constructor `ListFormat` creates and returns a formatter object that (depending on the configuration passed on creation) will join lists of strings using the best localized conventions.

I realize it's better shown than explained, so let's see an example:

```javascript
let engFormatter = new Intl.ListFormat('en', { style: 'short', type: 'unit' } );
engFormatter.format(["1","2","3"])   // "1, 2, 3"

let engFormatter = new Intl.ListFormat('en', { style: 'narrow', type: 'unit' } );
engFormatter.format(["1","2","3"])   // "1 2 3"

engFormatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' } );
engFormatter.format(["1","2","3"])   // "1, 2, and 3"

engFormatter = new Intl.ListFormat('en', { style: 'long', type: 'disjunction' } );
engFormatter.format(["1","2","3"])   //"1, 2, or 3"
```

The first optional argument for the `ListFormat` constructor is the language to be used - 'en' for English in our example. You can also pass an array of these BCP 47 language tags.

The second optional parameter is a _POJO_ with three (also optional) fields: 
- `"localeMatcher"` sets the locale matching algorithm to be used; it can be either `"lookup"` or `"best fit"` (which is the default one).
- `"style"`, which affects the separators used to join the input strings, and can be either:
    * `"long"`: `.format(["1", "2", "3"])` will result in `"1, 2, and 3"` (assuming that's the only option used).
    * `"short"`: `.format(["1", "2", "3"])` _should_ result in `"1, 2, 3"` (but in Chrome, it outputs `"1, 2, & 3"`);
    * `"narrow"`: `.format(["1", "2", "3"])` _should_ result in `"1 2 3"`.
- `"type"`, regulates the format of the output message; it can be:
    * `"conjunction"`, if we are trying to say that all the items in the list should be included (hence uses `"and"` before the last item, when style is "long");
    * `"disjunction"`, if we'd like to say that any of the items listed can be included (hence uses `"or"` before the last item, when style is "long");
    * `"unit"`, which doesn't use any separator for the last string. This option is the only officially valid option when `"style"` is set to `"short"` or `"narrow"`.

At least in Chrome's current implementation (version 86), however, the behavior when mixing `type` and `style` options is not always the expected one.

# New options for `Intl.DateTimeFormat`:  `dateStyle` and `timeStyle`

[`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) is a constructor for a language-sensitive date and time formatter, long-supported in the JavaScript ecosystem.

These new options allow to control the length of the local-specific formatting of date and time strings.

Let's see how to use it with times...

```javascript
let formatter = new Intl.DateTimeFormat('en' , { timeStyle: 'short' });
formatter.format(Date.now()); // "12:12 PM"

formatter = new Intl.DateTimeFormat('en' , { timeStyle: 'medium'})
formatter.format(Date.now()) // "12:12:57 PM"

formatter = new Intl.DateTimeFormat('en' , { timeStyle: 'long' })
formatter.format(Date.now()) // "12:12:36 PM GMT-5"
```

...and dates:


```javascript
formatter = new Intl.DateTimeFormat('us' , { dateStyle: 'short' });
formatter.format(Date.now()); // "10/27/20"

formatter = new Intl.DateTimeFormat('us' , { dateStyle: 'medium' });
formatter.format(Date.now()); // "Oct 27, 2020"

formatter = new Intl.DateTimeFormat('us' , { dateStyle: 'long' });
formatter.format(Date.now()); // "October 27, 2020"
```

Obviously you can also combine the two options to get a date-time string:

```javascript
formatter = new Intl.DateTimeFormat('uk' , { 
    timeStyle: 'long',
    dateStyle: 'short'
});
formatter.format(Date.now()); // "27.10.20, 12:20:54 GMT-5"
```

# Logical Operators and Assignment Expressions

Finally, this new draft is about to make it official some already widely-supported assignment operators like `||=` and `&&=`.