ECMAScript 2020 biggest new features
Tags: javascript, webdev
Published: 2020-06-17 08:41:23

It's June and this means that the new 11th edition of  [ECMA-262](https://tc39.es/ecma262/)  standard defining the  **ECMAScript**  and thus JavaScript language will be out shortly. Now, as you might know from my previous article about  [ECMAScript](https://areknawo.com/javascript-is-dead-long-live-ecmascript/)  and the one about  [ES2019 features](https://areknawo.com/ecmascript-2019-and-beyond/), JavaScript, ever since the introduction of ES6, is experiencing kind-of a  **rolling release cycle**. This means that while new editions of the ES specification go through the whole proposal, discussion, approval and finalization  [process](https://tc39.es/process-document/), individual features often appear much earlier in different browsers than the yearly specification release.

With that said, it's still a nice thing to have this moment in a year that you can say which new JS features are here for sure. And even though most web developers won't use all those features right away due to compatibility concerns, it's good to keep an eye on where the language is going.

And so, in this article, we'll go over the biggest of the new features introduced with ES2020.

# BigInt

You might have heard of it already. [`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) is a new 7th  **primitive data type**  in JavaScript and arguably the biggest new addition of ES2020. It's meant to allow developers to work with some really  **big integers**.

The biggest integer "usual"  `number`  type can handle is equal to  `2 ** 53 - 1`  or  `9007199254740991`. You can access this value under the  `MAX_SAFE_INTEGER`  constant.

```javascript
Number.MAX_SAFE_INTEGER; // 9007199254740991
```

As the name implies, operating on numbers above this value can be... quirky. With  `BigInt`s, there are  **no limits**  - except for your device's memory.

To define a  `BigInt`  you can either use the  `BigInt()`  function with a string representation of your big value or syntax similar to usual numbers, but followed by  `n`.

```javascript
const myBigInt = BigInt("999999999999999999999999999999");
const mySecondBigInt = 999999999999999999999999999999n;

typeof myBigInt; // "bigint"
```

It's important to know that  `BigInt`s aren't fully compatible with "usual"  `number`s. This means that you'll most likely want to use  `BigInt`s only when you know for certain that you'll be dealing with really big numbers.

```javascript
const bigInt = 1n; // small number, but still of BigInt type
const num = 1;

num === bigInt; // false -> they aren't strictly equal
num == bigInt; // true
num >= bigInt; // true -> they can be compared
num + bigInt; // error -> they can't operate with one another
```

Overall,  `BigInt`s are great for all those who do some complex math with JS. They do a great job of replacing quirky and slow libraries dedicated to the sole purpose of working with big numbers. Or at least integers, as we still haven't heard much of the  [`BigDecimal`  proposal](https://github.com/tc39/proposal-decimal).

As for the support, it's already  [pretty good](https://caniuse.com/#feat=bigint)  (for a new feature) with different Chromium-based browsers and Firefox having it for a few versions now. Only Safari lags behind.

# Dynamic imports

Similarly to  `BigInt`s,  [dynamic imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports)  is a feature you might be familiar with. Maybe that's because it was introduced to V8 way back in  [late 2017](https://v8.dev/features/dynamic-import)!

Anyway, dynamic imports, as one might expect, are meant to allow easier  **code-splitting**  natively in the browser. Instead of bundling, loading your modules statically, or using some clever AJAX tricks, you can now use the  `import`  keyword function-like syntax -  `import()`  to load your modules dynamically.

```javascript
import("module.js").then((module) => {
  // ...
});
// or
async () => {
  const module = await import("module.js");
};
```

The  `import()`  results in a promise resolving with the content exported by the loaded module. Thus, it can be used either with the ES6  `.then()`  method, or the newer  `async`/`await`.

Like I've said, the support is already  [very good](https://caniuse.com/#feat=es6-module-dynamic-import)  across all the major browsers.

# Nullish coalescing operator

Now we're starting to talk about some truly new stuff!  [Nullish coalescing operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator)  (`??`) is a new JS operator allowing basically to provide a  _"default value"_  when the accessed one is either  `null`  or  `undefined`. Check it out:

```javascript
const basicValue = "test";
const nullishValue = null;

const firstExample = basicValue ?? "example"; // "test"
const secondExample = nullishValue ?? "example"; // "example"
```

Alright, but you might be asking - how does that differ from the  [logical OR operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR)  aka double-pipe (`||`)? Well, the difference is actually very simple. Logical OR would use the second operand every time the first one is determined to be  **falsy**  - which in JavaScript means  `false`,  `0`, or  `""`, while also counting in  **nullish**  values -  `null`  and  `undefined`. On the other hand, nullish coalescing operator only uses the second operand when the first one is nullish - not falsy. Thus, if your code requires you to consider any value other than  `null`  or  `undefined`  as viable, then the this new operator is your best bet.

```javascript
const falseValue = false;
const zeroValue = 0;
const emptyValue = "";
const nullishValue = null;

const firstExampleOR = falseValue || "example"; // "example"
const secondExampleOR = zeroValue || "example"; // "example"
const thirdExampleOR = emptyValue || "example"; // "example"
const forthExampleOR = nullish || "example"; // "example"

const firstExample = falseValue ?? "example"; // false
const secondExample = zeroValue ?? "example"; // 0
const thirdExample = emptyValue ?? "example"; // ""
const forthExample = nullish ?? "example"; // "example"
```

Support for this operator is  [pretty decent](https://caniuse.com/#feat=mdn-javascript_operators_nullish_coalescing)  - most if not all of the latest versions of major browsers support it and additionally it can be easily transpiled with tools like  [Babel](https://babeljs.io/docs/en/babel-plugin-proposal-nullish-coalescing-operator), or used with  [TypeScript](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#nullish-coalescing).

# Optional chaining operator

Similarly to the nullish coalescing operator, the  [optional chaining operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)  also deals with  `null`  and  `undefined`  - but this time in objects. Whereas previously trying to access a property on a nullish value would result in an error, now optional chaining operator (`?.`) would simply continue to "return" the nullish value.

```javascript
const obj = {
  prop: {
    subProp: {
      value: 1,
    },
  },
};

obj.prop.subProp.value; // 1
obj.prop.secondSubProp.value; // error

obj?.prop?.subProp?.value; // 1
obj?.prop?.secondSubProp?.value; // undefined
```

Of course, this is nothing more than just some  **syntactic sugar**  but it's a welcome addition nonetheless. Just remember to not flood your code with these operators - they're nice but still have a very tiny higher impact on performance than a usual  `.`. And it's even more so if you're using this through transpilation in  [Babel](https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining)  or  [TypeScript](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining), which is also possible.

As for the browser support - it's basically  [the same](https://caniuse.com/#feat=mdn-javascript_operators_optional_chaining)  as for nullish coalescing operator - so fine, but nothing special.

# GlobalThis

Now, because of JavaScript's  [omnipresence](https://areknawo.com/where-javascript-is-used/), the same code is often expected to work across many different environments, such as the browser, Node.js, or Web Workers. And even though achieving this cross-compatibility is never an easy task, it just got a little bit easier thanks to  [`globalThis]`(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis).

`globalThis`  is a new global property that always refers to the current environment's  **default global**  object. This means  `self`  for Web Workers,  `window`  for browsers,  `global`  for Node.js, and anything else for any runtime that correctly implements the ES2020 standard.

```javascript
// Hacky globalThis polyfill you had to use pre-ES2020
const getGlobal = () => {
  if (typeof self !== "undefined") {
    return self;
  }
  if (typeof window !== "undefined") {
    return window;
  }
  if (typeof global !== "undefined") {
    return global;
  }
  throw new Error("Couldn't detect global");
};

getGlobal() === globalThis; // true (for browser, Web Worker and Node.js)
globalThis === window; // true (if you're in browser)
```

`globalThis`  is already  [pretty well-supported](https://caniuse.com/#feat=mdn-javascript_builtins_globalthis)  across all major browsers, and there are external  [polyfills](https://github.com/es-shims/globalThis)  available for use in older environments.

# Promise.allSettled()

Like most previous releases, ES2020 not only adds entirely new features but also improves on the old ones. So is the case with Promises which gained new  [`Promise.allSettled()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled)  method. It's similar to the already existent  `Promise.all()`  method in a way that it returns a promise that resolves when all passed Promises are  _"settled"_, but with 1 major difference. Unlike  `Promise.all()`, which resolves when all passed Promises resolves and fails when only a single Promise fails,  `Promise.allSettled()`  resolves always when every Promise is settled -  **no matter**  if it resolved or failed. Hence the name.

```javascript
const promises = [
  new Promise(() => {
    /* ... */
  }),
  /* ... */
];

Promise.allSettled(promises).then(() => {
  console.log("All promises have settled!");
});
```

`Promise.allSettled()`  has  [good support](https://caniuse.com/#feat=mdn-javascript_builtins_promise_allsettled)  and is polyfillable with libraries like core-js (only applicable to  [newer versions](https://github.com/zloirock/core-js/issues/793)).

# String.matchAll()

Another new improvement-like method is  [`String.matchAll()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll). Basically, if you've ever worked with  [RegExps](https://areknawo.com/hello-regexp/)  before,  `String.matchAll()`  is a nice alternative to using  `RegExp.exec()`  in a  `while`  loop with the  `g`  flag enabled. That's all there's to it. It returns an  [iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)  (not to be confused with full-blown arrays) that contains all the match results - including capturing groups.

```javascript
const regexp = /t(e)(st(\d?))/g;
const str = "test1test2";
const resultsArr = [...str.matchAll(regexp)]; // convert iterator to an array

resultsArr[0]; // ["test1", "e", "st1", "1"]
resultsArr[0]; // ["test2", "e", "st2", "2"]
```

The support is  [good](https://kangax.github.io/compat-table/es2016plus/#test-String.prototype.matchAll)  and the feature can be easily polyfilled with the method I described earlier.

# For-in order

Lastly, we've got just a minor tweak to the specs that now strictly defines the order in which the  `for..in`  loop should iterate. It was already handled pretty well by the browsers themselves, so it's just a matter of making it official.

# Bottom line

As you can see, there are some interesting new features when it comes to ES2020. Most of them have pretty good cross-browser support already and we can suspect that it'll only get better as the time goes on. However, let's face it - there are no  _"ground breaking"_  features in this release, and web developers are unlikely to utilize any of them to their full extent. The need to support older browsers is  [limiting](https://areknawo.com/why-you-should-have-ditched-ie-support-long-ago/), and when you factor in all the necessary work and polyfills and transpilation that your code would require, there's simply not good enough reason to justify this trade-off.

So, if you support only the newest browsers I say good for you. Just use the new features however you want. But, if you want broader support, then I think the choice is up to you.

Anyway, if you enjoyed the content consider  **following me**  on  [Twitter](https://twitter.com/areknawo),  or [Facebook](https://facebook.com/areknawoblog) for more up-to-date web dev stuff. Thanks for reading and happy coding!

<a href="https://www.buymeacoffee.com/areknawo" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" style="height: 25px !important;width: 100px !important;" ></a>