Categories
Codes Javascript

New Exciting Features in JavaScript ES2020

“It’s not enough to be up to date, you have to be up to tomorrow.”

David Ben-Gurion

Introduction

JavaScript is one of the mostly used programming languages in the world. It is regularly updated, and the latest is ES2020.

It is important for programmers who uses JavaScript in daily basis to know about the updates so they can keep their skills sharp. If you are not one of them, it’s also good to know about these updates because you might change your mind someday if you find something interesting.

Without further ado, here are some of the new features in ES2020 which, in my opinion, are exciting and could help us to code better:

BigInt

By using BigInt, you can now use a larger number than Number.MAX_SAFE_INTEGER, which is pow(2,53)-1, which equals 9007199254740992. But, remember that it’s not backward compatible because of the traditional number system IEEE754.

Using BigInt is quite simple, just add an n behind the numbers like this:

let num = Number.MAX_SAFE_INTEGER; // 9007199254740992
++num // 9007199254740992


let num = 9007199254740992n;
++num; // num = 9007199254740993n
num += 3; // num = 9007199254740996n

Optional Chaining

In your journey programming a JavaScript code, surely you have encountered something like this:

const obj = {
  propA: {
    propAA: 'propAA'
    propAB: {
      propABA: 'it exists!' 
    }
  }
};

if (obj && obj.propA && obj.propA.propAB && 
    obj.propA.propAB.propABA){
  console.log(obj.propA.propAB.propABA); // it exists!
}

console.log(obj.propA.propAC.test) 
// Error: Cannot read property "test" of undefined 

See the “if” part? It’s ridiculously long, isn’t it? Checking every level in the object is necessary to prevent an error as in the statement console.log(obj.propA.propAC.test).

Optional Chaining solves this problem in an elegant way by just adding the operator ?. in every level of properties in the object. It allows you to safely access the nested properties without the need of checking whether it exists or not.

The example above can be simplified with the optional chaining feature like below:

const obj = {
  propA: {
    propAA: 'propAA'
    propAB: {
      propABA: 'it exists!' 
    }
  }
};

console.log(obj?.propA?.propAB?.propABA); // it exists!

console.log(obj?.propA?.propAC?.test) // undefined

So many lines of checking the properties’ existence are removed elegantly by just using the ?. operator. And no error is thrown here. Beautiful, isn’t it?

Nullish Coalescing

The || operator in JavaScript can be used for checking false-y values such as 0, '', undefined, and null. If the values in the variable are of those, then it will return the default value you set. As an example, see the code below:

const valSomething = "Something";
const valZero = 0;
const valEmpty = '';
const valUndefined = undefined;
const valNull = null;

console.log(valSomething || 'NONE'); // Something
console.log(valZero || 'NONE'); // NONE
console.log(valEmpty || 'NONE'); // NONE
console.log(valUndefined || 'NONE'); // NONE
console.log(valNull || 'NONE'); // NONE

But, what if you only want to check if the value is undefined or null only? Because as you can see, valZero and valEmpty actually has a value: 0 and '' respectively.

ES2020 introduces us to nullish coalescing. To put it simply, this feature helps you to return the default value you set when you are checking whether the value contained in variable is null or undefined using the operator ??. Here is an example:

const valSomething = "Something";
const valZero = 0;
const valEmpty = '';
const valUndefined = undefined;
const valNull = null;

console.log(valSomething ?? 'NONE'); // Something
console.log(valZero ?? 'NONE'); // 0
console.log(valEmpty ?? 'NONE'); // ''
console.log(valUndefined ?? 'NONE'); // NONE
console.log(valNull ?? 'NONE'); // NONE

As shown in the example above, only variables which contain undefined or null will be returned the set default value.

Promise.allSettled()

You might be already familiar with Promise.all(), which returns a Promise that fulfills when all the Promises has already been fulfilled. But Promise.all() will throw an error if one of the promises are rejected, and therefore will terminate the process of fulfilling midway, ignoring the already fulfilled and unfulfilled promises which were already made.

See the snippet below as an example:

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = Promise.reject('error');
const promise4 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

const promises = [promise1, promise2, promise3, promise4];

Promise.all(promises).then((values) => {
  console.log(values);
}).catch((err) => { 
  console.log(err); 
});

// Actual output: "error"

From the example above, promise3 rejects, so it throws an error.

Promise.allSettled() will complete the promises, whether it resolves or rejects. The mantra is: “Run them all without caring the results.” The snippet below explains more clearly:

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = Promise.reject('error');
const promise4 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, 'foo');
});

const promises = [promise1, promise2, promise3, promise4];

Promise.allSettled(promises).then((values) => {
  console.log(values);
});

// Output:
// Array [
//   Object { status: "fulfilled", value: 3 }, 
//   Object { status: "fulfilled", value: 42 }, 
//   Object { status: "rejected", reason: "Error" },
//   Object { status: "rejected", reason: "foo" }
// ]

promise3 and promise4 are rejected, but still settled as part of the returned Array.

String.matchAll()

This method returns an iterator of all results matching a string with a regular expression. In other words, it returns the capturing group in addition of the matched parts of string. Basically, it’s an improvement from String.match() and RegExp.exec().

See the snippet below as an example:

const regex = /c(he)(ck(\d?))/g;
const str = 'check1check2';

const matchAll = str.matchAll(regex);
const result = [...matchAll];

// output result (an array of array of string,)
result.forEach((res, index) => {
  document.getElementById('result'+index).innerHTML = res;
});

// expected output 0: "check1", "he", "ck1", "1"
// expected output 1: "check2", "he", "ck2", "2"

Dynamic Imports

Dynamic imports help you to lazy load the modules when needed. It’s very useful for rarely used features and functions, as it will reduce page load times. Prior to ES2020, this was not possible, because all modules must be imported whether it’s used or not.

Here is an example on how to use the feature:

const moduleName = './module2.js';

// Using "then"
import(moduleName).then((module) => {
  module.doThis();
  module.doThat();
});

// Using async-await
const importModule = async () => {
  const module = await import(moduleName);
  module.doThis();
  module.doThat();
};
importModule();

Conclusion

There you have it. It’s amazing how JavaScript has grown through all the years. I remember I hated to code with JavaScript when I was still a student, but now I love it! It has become a part of my daily life.

These new features might not be essential in your code development, but it is good to learn the updates. Who knows, you might still remember it and are able to use some tricks out of it by making a seemingly complicated solution look simple.

Thanks for reading! Always live your code and code your life!

“Update your knowledge. Some knowledge goes out of date quickly. Technology, countries, societies, cultures, and religions are constantly changing.”

Hans Rosling, Factfulness: Ten Reasons We’re Wrong About the World – and Why Things Are Better Than You Think

By Ericko Yap

Just a guy who is obsessed to improve himself. Working as a programmer in a digital banking company. Currently programming himself in calisthenics, reading books, and maintaining a blog.

Leave a Reply

Your email address will not be published. Required fields are marked *