Blog post cover
Arek Nawo
16 Jun 2020
9 min read

ECMAScript 2020 biggest new features

It’s June and this means that the new 11th edition of ECMA-262 standard defining the ECMAScript and thus JavaScript language will be out shortly. Now, as you might know from my previous article about ECMAScript and the one about ES2019 features, 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, 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.

Number.MAX_SAFE_INTEGER; // 9007199254740991

As the name implies, operating on numbers above this value can be… quirky. With BigInts, 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.

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

typeof myBigInt; // "bigint"

It’s important to know that BigInts aren’t fully compatible with “usual” numbers. This means that you’ll most likely want to use BigInts only when you know for certain that you’ll be dealing with really big numbers.

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, BigInts 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.

As for the support, it’s already pretty good (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 BigInts, dynamic imports is a feature you might be familiar with. Maybe that’s because it was introduced to V8 way back in late 2017!

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.

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 across all the major browsers.

Nullish coalescing operator

Now we’re starting to talk about some truly new stuff! 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:

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

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 - most if not all of the latest versions of major browsers support it and additionally it can be easily transpiled with tools like Babel, or used with TypeScript.

Optional chaining operator

Similarly to the nullish coalescing operator, the optional chaining operator 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.

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 or TypeScript, which is also possible.

As for the browser support - it’s basically the same as for nullish coalescing operator - so fine, but nothing special.

GlobalThis

Now, because of JavaScript’s omnipresence, 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.

// 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 across all major browsers, and there are external polyfills 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.

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

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

Promise.allSettled() has good support and is polyfillable with libraries like core-js (only applicable to newer versions).

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 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 (not to be confused with full-blown arrays) that contains all the match results - including capturing groups.

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 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, 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, Facebook, or through my newsletter for more up-to-date web dev stuff. Thanks for reading and happy coding!

If you need

Custom Web App

I can help you get your next project, from idea to reality.

© 2024 Arek Nawo Ideas