Abhinav Anshul
26 Nov 2021
•
6 min read
The world of JavaScript( officially ECMAScript ) changes fast and with awesome features getting added each year, it's certainly hard to keep up. The last big overhaul happened in ES2015 when a bunch of new features, sugarcoated syntax were added to the language.
ECMAScript community releases new features every year. In this post, we will have a look at 7 key features that were introduced in ES2020
Let's get into each one by one
BigInt
is a special data type introduced in ES2020 to define numbers that exceed the Number
data type range. The Number
data type limits between -(2^53-1) to -(2^53-1), which we can check using
Number.MAX_SAFE_INTEGER
& Number.MIN_SAFE_INTEGER
.
console.log(Number.MIN_SAFE_INTEGER);
// -9007199254740991
console.log(Number.MAX_SAFE_INTEGER);
// 9007199254740991
Introduction to BigInt
now increases the total number of primitive
data type to 8
, the rest being,
To use BigInt, we simply append n
at the very end of an integer, and that would be parsed as a BigInt.
BigInt("1") // -> 1n
typeof(1n) // -> "bigint"
BigInt is useful in situations where we have to essentially deal with large integers that are beyond the scope of Number
type.
console.log(18014398509481982n + 18014398509481982n) // -> 36028797018963964n
All arithmetic operations are valid, the only exception to this is unary plus operator
console.log(1n + 1n) // -> 2n
console.log(7n / 3n) // -> 2n
console.log(+10n) // -> Unary Operator error
The same goes for comparison as well, however, strict equality doesn't hold when compared with the Number
data type.
10n == 10 -> True
10n === 10 -> False
The Nullish Coalescing operator is not something new but rather a sugarcoated version of a pattern we have been doing for quite a long time.
Have a look at this piece of code :
if (a !== null && a !== undefined){
return a;
}
else {
return b;
}
// Now, nullish coalescing operator would simplify the above logic to :
a ?? b
In simple terms,
??
returns the first argument if it is defined i.e neither null
nor undefined
??
returns the second argument if the first argument is either null
or undefined
Confused? Well let's have a look at few examples to clear up things
1 ?? 2 // 1 (returns the first argument as its neither null nor undefined)
undefined ?? 1 // 1 (returns the second argument as the first one is undefined)
// we can even chain up several arguments as well, and the operator would return the first `defined value`
var country = null;
var state = undefined;
var city = "London";
console.log(country ?? state ?? city) // London (returns the first `defined value`)
Let's say if a user is logged-in, display the first name else display "Anonymous" :
When logged-in
let user = "Alice"
console.log(user ?? "Anonymous"); // Alice
When not logged-in
let user = undefined;
console.log(user ?? "Anonymous"); // Anonymous
The Optional Chaining Operator introduced in ES2020 is similar to the .
(dot operator). It solves a very specific problem and is useful when we need a property that is nested deep in an object.
Consider the example below :
let response = {
type : "customer",
isSignedIn : true,
details : {
age : 22,
name : "Lucy",
Country : "Estonia"
}
}
Given the above example is an API response and we aren't sure if the property we are accessing even exists inside the object or not, then traditionally we have to do something like below to make sure we don't have any nullish
value when nesting inside the response
object properties
const data = response.details && response.details.name
However, with Optional Chaining, we can do :
const data = response.details?.name
JavaScript makes sure at each level of the object the property is not nullish (null or undefined), hence proving ?.
much practical than plain ol' dot operator.
Gotchas with :
Optional Chaining throws an error on the left-hand side of an assignment.
The very first property before the ?.
must be defined and cannot be invalid.
Similar to ?.
we have slightly different variants too :
?.[] => calling arrays
?.() => calling functions
Think about the number of environments and platforms we run JavaScript in, browsers, smartphones, servers, robotics equipment. For each environment, the JavaScript Object model is different and the global object might point to a different property.
In browsers, the global object could be window
, self
or frame
, depending on the context. However there is no scope of the above-mentioned properties in NodeJs, hence it uses global
to point to its global object.
Can we see the chaos here? If we have to run our js
code in a range of different environment we need to figure out its global object first and is exactly what we have been doing for so long.
A common pattern to figure out the global object is as :
function getGlobalObject() {
if (typeof globalThis !== 'undefined') { return globalThis; }
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('cannot find the global object');
};
if (typeof getGlobalObject().Promise.allSettled !== 'function') {
// the Promise.allSettled() Not available in this environment
}
However, the above solution has its own limitations and trade-offs.
globalThis
tries to address the issue by pointing globalThis
keyword to its global object model irrespective of the environment(as it is available everywhere). It was introduced in 2020 and currently stands on stage 4 and is available on most browsers.
// nodeJS
console.log(globalThis) // returns the global object
// browsers
console.log(globalThis) // returns the window object
// web-workers
console.log(globalThis) // returns the global web workers context
With the introduction of globalThis
, it would be best practice to stop using this
keyword in the context of global objects and start using globalThis
instead.
Promise.allSettled()
is a method that takes an iterable object(array) and returns a promise when all the provided promises have either resolved or rejected, meaning it does not short-circuit in the middle.
The returned outcome is an object with two things :
_a) value -> If the status is fulfilled._
_b) reason -> If the status is rejected._
It's hard not to talk about the ES2015's Promise.all
and its similarities/dissimilarities with allSettled
A striking difference between these two would be all
short-circuits in the middle if any of the provided promises is rejected, while allSettled
waits for async result and filters them by status and never shortcircuits.
Let's have a look at its working :
const promise1 = new Promise((resolve) => resolve("yay"));
const promise2 = new Promise((resolve, reject) => reject("oh-no"));
(async () => {
try {
const result = await Promise.allSettled([promise1, promise2]);
console.log(result);
} catch (error) {
console.error(error);
}
})();
// Output:
// [
// { status: 'fulfilled', value: 'yay' },
// { status: 'rejected', reason: 'oh-no' },
// ]
If we compare it with all
:
const promise1 = new Promise((resolve) => resolve("yay"));
const promise2 = new Promise((resolve, reject) => reject("oh-no"));
(async () => {
try {
const result = await Promise.allSettled([promise1, promise2]);
console.log(result);
} catch (error) {
console.error(error);
}
})()
// Output:
// Error : "oh-no"
The amount of JS heavy apps we ship these days can be quite overwhelming and with these lot of javascript files, the module import/export should be effective.
ES2020's dynamic import addresses this issue to make the page load ups, first meaningful paint etc efficient and fast. This is done by dynamically importing the files that we need at that point in time.
The import
keyword was introduced in ES2015
, and we have been importing modules like
import React from 'react';
ES2020 allows us to use import
as a function (although it looks like a function, it is not)
// we dont need to set type of module below
<script>
import('./add.js')
.then(module => module.default(3, 7)) //returns 10
.catch(error => // log error here);
</script>
The above piece of code makes sure the add.js
module is only imported when we need to sum up two numbers. It doesn't unnecessarily bloat up the js code which could make page loads slow.
matchAll
is a new method that is added to the string prototype. This returns an iterator matching against a regular expression that we have given.
A simple example to demonstrate the same :
const test = "climbing, oranges, jumping, flying, carrot";
const regex = /([a-z]*)ing/g;
const matches = [...test.matchAll(regex)];
const result = matches.map(match => match[1]);
// outputs the following :
["climb", "jump", "fly"]
While we just finished learning ES2020, the ES2021 has already been drafted for its next release. Here's what's in the box for us :
Some Important Resources that I have collected over time:
i. https://auth0.com/blog/javascript-whats-new-es2020/
ii. https://www.martinmck.com/posts/es2020-everything-you-need-to-know/
iv. https://blog.tildeloop.com/posts/javascript-the-difference-between-match-and-matchall
Loved this post? Have a suggestion or just want to say hi? Reach out to me on Twitter
Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ
108 E 16th Street, New York, NY 10003
Join over 111,000 others and get access to exclusive content, job opportunities and more!