“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
You might be interested in these posts: