What's new in ES2020 
Tags: javascript, node, webdev, html
Published: 2020-10-18 17:31:13

We are closing down to the end of the year, 6 months into the approval of ES2020 specifications - and likely at least 6 months away from ES2021.

Before discussing rumors about next year's release, let's recap what was introduced in ES11, aka ES2020.

You can check how each feature is supported [here](https://kangax.github.io/compat-table/es2016plus/):

{% github https://github.com/kangax/kangax.github.com %}

# The nullish coalescing operator `??`

It's used to provide a default value in place of null and undefined (only).
Fixes the abuse of ||, which defaults on any falsy!

```javascript
// With ?? the left operand is returned only with null and undefined
null ?? 1       // 1
undefined ?? 1  // 1
false ?? 1      // false
0 ?? 1          // 0
"" ?? 1         // ""
2 ?? 1          // 2
"a" ?? 1        // "a"
true ?? 1       // true 	

// With || the left operand is returned only with all falsey values
null || 1       // 1
undefined || 1  // 1
false || 1      // 1
0 || 1          // 1
"" || 1         // 1
2 || 1          // 2
"a" || 1        // "a"
true || 1       // true
```

It's well supported already in [browsers](https://caniuse.com/mdn-javascript_operators_nullish_coalescing) and NodeJs (from version 14).

## Logical nullish assignment (??=) 

The assignment version of the `??` operator is also introduced and [supported](https://caniuse.com/mdn-javascript_operators_logical_nullish_assignment).

It looks like your regular assignment operators: `x ??= y`, which reminds a lot logical assignment operators like `||=`.

`x ??= y`, however, only assigns a new value to `x` if `x` is nullish (`null` or `undefined`).

Check the difference:

```javascript
const car = {  speed: '150mph' };

car.speed ??= '241.4kmh';    // nothing changes

car.doors ??= 0;             // add property doors and set it to 0
car.doors ??= 3              // nothing changes, 0 isn't nullish     
car.doors||= 3               // sets cars.doors to 3
```

As you can see, it's particularly useful for objects, when you aren't sure if a property has been defined already and you don't want to risk overwriting it (but it can also be used with variables, like `car ||= {};`).

# Optional Chaining `?.`

Allows access to nested object properties without worrying if the properties exist or not.

```javascript
const car = {  speed: { value: 150, unit: 'mph'}, doors: 5 };

car.wheels.value             // TypeError: Cannot read property 'value' of undefined
car.wheels?.value            // undefined

car.speed?.value             // 150
car.speed?.whatever          // undefined 
car.speed?.whatever?.value   // undefined
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/op2840pdrwmc8n8koag5.png)

Did you know that it can also be used for function calls?
Like this: 

```javascript
const car = {  
    speed: { 
        value: 150,
        unit: 'mph'
    },
    doors: 5,
    brake: x => console.log('braking')
};

car.accelerate        // TypeError: car.accelerate is not a function
car.accelerate?.()    // undefined

car.brake?.()         // logs "braking"
```

The coolest part is, this works so nicely together with the nullish coalescing operator, providing defaults whenever the property chain doesn't exist!

```javascript
const car = {  speed: { value: 150, unit: 'mph'}, doors: 0 };

let wheels = car.wheels?.value ?? 4;     // 5
let doors = car.doors?.value ?? 3;       // 0, doors is not nullish!
```

Optional chaining is supported in modern [browsers](https://caniuse.com/mdn-javascript_operators_optional_chaining) and NodeJs (starting with version 14).

# `globalThis`

A single global object valid and consistent across all JS platforms.

Why is it important? Before ES2020, it was madness when you had to write cross-platform JavaScript referencing the global object.

You had to use:
- `window` on browsers
- `global` in NodeJs
- `self` for web workers

Now instead, it works like a charm.

This feature is already supported in [browsers](https://caniuse.com/mdn-javascript_builtins_globalthis) and, of course, in NodeJs: let's double check...

In Chrome's console (check support [here](https://caniuse.com/mdn-javascript_builtins_globalthis)):

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/wmbskit69hnrmtdfsy0n.png)


In NodeJs (since version 12.0.0):![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/e5db7mz6jwgz9020scc5.png)

# `String.prototype.matchAll`

The `matchAll` method for strings allows you to iterate through all matched groups of a regular expression.

```javascript
const regex = /([a-z]+)(\d*)/g;
const txt = "abc1 def ABC WXYZ xyz22 !§ $%& #|";

for (const w of txt.matchAll(regex)) {
    console.log(w);
}
```

![Example of matchAll](https://dev-to-uploads.s3.amazonaws.com/i/kwsow8s3apjvlccf081m.PNG)

With respect to  `String#match` it allows access to the capture groups, which is particularly convenient to extract info from matched strings! For instance, for an email, you could more easily get username and domain.

Before `matchAll`, you still could get the same result, but you would have needed to run a loop where you called `RegExp.exec`

```javascript
while (true) {
    const match = regex.exec(txt);
    if (match === null) {
        break; 
    }
    console.log(match);
}
```

![Example of RegExp.exec](https://dev-to-uploads.s3.amazonaws.com/i/l7tf5t4qv0gn92ipn9pe.PNG)

This feature is supported in NodeJs since version 12.0.0, and also now widely supported in [browsers](https://caniuse.com/mdn-javascript_builtins_string_matchall).

# `Promise.allSettled`
This new method takes an array of Promises and resolves once *all* of them are settled, one way or another (either resolved or rejected).

You can hence run a group of promises in parallel, but get a single "exit point" when all of them are completed - in practice `allSettled` created a new promise that is fulfilled when all of the original promises are either fulfilled or rejected.

Suppose you have a function that returns a `Promise`, something like `fetch`, which makes a http call.

We can use it to create an example where we have an array with 2 or more promises, and implement an action that will be performed only when all of these promises are settled:

```javascript
Promise.allSettled([fetch('http://www.google.com'), fetch('http://www.twitter.com')])
    .then(results => {
        console.log('All settled', results)
    });
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/dynkae291nvwkjzf6ijw.png)

(Obviously the array passed to `allSettled` can also have promises with a completely different nature and origin, and that can take very different time to settle).

NodeJs supports this method since version 12.9, while you can check out support in browsers [here](https://caniuse.com/mdn-javascript_builtins_promise_allsettled).

> Method `Promise.all` was already defined in ECMAScript specification, with a similar behavior and just a small, yet significant, difference: it will reject as soon as any of the promises passed is rejected.

# BigInt

Finally JavaScript is introducing arbitrary-precision integers!

Before ES2020 the largest integer that could be represented and stored in JS was 2^53-1

```javascript
let n = Number.MAX_SAFE_INTEGER;    // 9007199254740991
++n                                 // 9007199254740992
++n;                                // Still 9007199254740992!
```

Now the limit is your RAM! 😁
```javascript
n = BigInt(Number.MAX_SAFE_INTEGER);    // 9007199254740991n
++n                                     // 9007199254740992n
++n;                                    // 9007199254740993n
```

Well, at least in theory, since each JS engine needs to put a limit, while implementing it, to the maximum size a BigInt can take - for instance, for V8, it's apparently around 16K (😱) bytes (which is still a lot!).

As you might have noticed, BigInts have a peculiarity, there is an 'n' appended at the end of the number's digits; when you declare a BigInt, to distinguish it from a regular int you also have to add that trailing 'n'. You can highlight the difference by checking the type returned with `typeof`.

```javascript
let m = 9007199254740991n;
let bigInt=1n;
let num = 1;
typeof(bigInt);              // 'bigint'
typeof(num);                 // 'number'
```

As you can see in the my first example, it's also possible to convert an existing int value or variable:

```javascript
let num = BigInt(4);   // 4n
let m = 42;            // 42
num = BigInt(m);       // 42n
```

Be careful, though, because the value to convert **must** be an integer, you can't pass a floating point to `BigInt` constructor:

![BigInt(1.1) throws a RangeError](https://dev-to-uploads.s3.amazonaws.com/i/6q70t41b7kbwg92xqow5.png)

For the same reason, you cannot mix BigInts and numbers in expressions: while it's kind of obvious for floating points, there is no automatic conversion for integers either:

![Mixing numbers and bigints throws a TypeError](https://dev-to-uploads.s3.amazonaws.com/i/i2jou68w0klptatwy1wf.PNG)

So, you'll also need to explicitly convert integer values to BigInt:

```javascript
let n = 4;
let m = BigInt(3n);
n * m          // TypeError: Cannot mix BigInt and other types, use explicit conversions
BigInt(n) * m  //12n
```

How is it supported, you might ask: NodeJs supports them since version 10.4.0, check out here for [browsers](https://caniuse.com/bigint).

# Dynamic import

Dynamic import in JavaScript allows you to dynamically import JavaScript modules (or more in general, JavaScript files as modules) in your application. Before ES2020, you could do dynamic import through bundlers; now, this is supported natively.

```javascript
let mymodule;
if (Math.random() < 0.5) {
    // Replace mymodule with a module you have installed!
    mymodule = import('mymodule');  
}
console.log(mymodule);
```

If you run this code snippet in node's console, half of the time it will print undefined, and half of the time the result of importing your module. You can leverage this to conditionally load one library or another (which, of course, only makes sense if their public interfaces are compatible, i.e. they expose the same methods... or if you find the right workaround).

For instance, something like:

```javascript
let jsonModule, jsonParse;

if (condition) {
    jsonModule = import('json');
    jsonParse = jsonModule.parseJson;
} else {
    jsonModule = import('myJson');  
    jsonParse = jsonModule.parse;
}

let json = jsonParse(jsonText);
```

Check out which browsers support it [here](https://caniuse.com/es6-module-dynamic-import).

# `import.meta`

We only have one last minor feature left to discuss for ES2020, the `import.meta` object.

This new property exposes context-specific metadata for a given  JavaScript module. These data contain information about the module, specifically, at the moment, the module's URL.

```bash
$>node --experimental-modules --es-module-specifier-resolution=node temp.mjs

[Object: null prototype] {
  url: 'file://**/temp.mjs'
}
```

How is it supported? NodeJs supported it since version 10.4, and for what concerns browsers... check it out [here](https://caniuse.com/mdn-javascript_statements_import_meta).