DEV Community

Cover image for What's new in ES2020
Marcello La Rocca
Marcello La Rocca

Posted on • Updated on

What's new in ES2020

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:

GitHub logo kangax /

List of my projects and resume

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!

// 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
Enter fullscreen mode Exit fullscreen mode

It's well supported already in browsers and NodeJs (from version 14).

Logical nullish assignment (??=)

The assignment version of the ?? operator is also introduced and supported.

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:

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
Enter fullscreen mode Exit fullscreen mode

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.

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
Enter fullscreen mode Exit fullscreen mode

Alt Text

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

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"
Enter fullscreen mode Exit fullscreen mode

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

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!
Enter fullscreen mode Exit fullscreen mode

Optional chaining is supported in modern browsers and NodeJs (starting with version 14).


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 and, of course, in NodeJs: let's double check...

In Chrome's console (check support here):

Alt Text

In NodeJs (since version 12.0.0):Alt Text


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

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

for (const w of txt.matchAll(regex)) {
Enter fullscreen mode Exit fullscreen mode

Example of matchAll

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

while (true) {
    const match = regex.exec(txt);
    if (match === null) {
Enter fullscreen mode Exit fullscreen mode

Example of RegExp.exec

This feature is supported in NodeJs since version 12.0.0, and also now widely supported in browsers.


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:

Promise.allSettled([fetch(''), fetch('')])
    .then(results => {
        console.log('All settled', results)
Enter fullscreen mode Exit fullscreen mode

Alt Text

(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.

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.


Finally JavaScript is introducing arbitrary-precision integers!

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

let n = Number.MAX_SAFE_INTEGER;    // 9007199254740991
++n                                 // 9007199254740992
++n;                                // Still 9007199254740992!
Enter fullscreen mode Exit fullscreen mode

Now the limit is your RAM! 😁

n = BigInt(Number.MAX_SAFE_INTEGER);    // 9007199254740991n
++n                                     // 9007199254740992n
++n;                                    // 9007199254740993n
Enter fullscreen mode Exit fullscreen mode

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.

let m = 9007199254740991n;
let bigInt=1n;
let num = 1;
typeof(bigInt);              // 'bigint'
typeof(num);                 // 'number'
Enter fullscreen mode Exit fullscreen mode

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

let num = BigInt(4);   // 4n
let m = 42;            // 42
num = BigInt(m);       // 42n
Enter fullscreen mode Exit fullscreen mode

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

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

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

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

How is it supported, you might ask: NodeJs supports them since version 10.4.0, check out here for browsers.

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.

let mymodule;
if (Math.random() < 0.5) {
    // Replace mymodule with a module you have installed!
    mymodule = import('mymodule');  
Enter fullscreen mode Exit fullscreen mode

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:

let jsonModule, jsonParse;

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

let json = jsonParse(jsonText);
Enter fullscreen mode Exit fullscreen mode

Check out which browsers support it here.


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.

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

[Object: null prototype] {
  url: 'file://**/temp.mjs'
Enter fullscreen mode Exit fullscreen mode

How is it supported? NodeJs supported it since version 10.4, and for what concerns browsers... check it out here.

Top comments (0)