JavaScript is one of the most popular programming languages, and features are now added to the language every year. This article covers new features added in ECMAScript 2020, also known as ES11.
Before the introduction of ECMAScript 2015, also known as ES6, JavaScript language development had a very slow pace. Fortunately, since then, new features are added every year. Be aware that not all features might be supported in all modern browsers yet, but thanks to transpilers like Babel, we can use them already today. This article will cover some of the latest additions to JavaScript — ECMAScript 2020 (ES11).
I’m sure that most developers are familiar with an error of this kind:
TypeError: Cannot read property ‘x’ of undefined
This error basically means that we tried to access a property on something that is not an object.
const flower = {
colors: {
red: true
}
}
console.log(flower.colors.red) // this will work
console.log(flower.species.lily) // TypeError: Cannot read property 'lily' of undefined
JavaScript engine will always throw an error in a scenario like this. However, there are cases in which it doesn't matter that the value isn't there yet, because we might know it will be. Fortunately, here is the optional chaining feature to the rescue!
We can use the optional chaining operator, which consists of a question mark and a dot: ?.
, to indicate that an error should not be thrown. Instead, if there is no value, undefined will be returned.
console.log(flower.species?.lily) // undefined
Optional chaining can also be used when accessing array values or calling a function.
let flowers = ['lily', 'daisy', 'rose']
console.log(flowers[1]) // daisy
flowers = null
console.log(flowers[1]) // TypeError: Cannot read property '1' of null
console.log(flowers?.[1]) // undefined
let plantFlowers = () => {
return 'orchids'
}
console.log(plantFlowers()) // orchids
plantFlowers = null
console.log(plantFlowers()) // TypeError: plantFlowers is not a function
console.log(plantFlowers?.()) // undefined
Until recently, whenever there was a need to provide a fallback value, the logical operator ||
had to be used. It works in most cases, but it can't be applied in some scenarios. For instance, if the initial value is a Boolean or a number. Let's take a look at an example below, where we want to assign a number to a variable, or default it to 7 if the initial value is not a number:
let number = 1
let myNumber = number || 7
The myNumber variable is equal to 1, because the left-hand value (number) is a truthy value, as 1 is a positive number. However, what if the number variable is not 1, but 0?
let number = 0
let myNumber = number || 7
0 is a falsy value, and even though it is a number, the myNumber variable will have the right-hand value assigned to it. Therefore, myNumber is now equal to 7. However, that's not really what we want. Fortunately, instead of writing additional code and checks to confirm if the number variable is indeed a number, we can use the nullish coalescing operator. It consists of two question marks: ??
.
let number = 0
let myNumber = number ?? 7
The right-hand side value will only be assigned if the left-hand value is equal to null or undefined. Therefore, in the example above, the myNumber variable is equal to 0.
Many programming languages that have classes allow defining class properties as public, protected, or private. Public properties can be accessed from outside of a class and by its subclasses, while protected classes can only be accessed by subclasses. However, private properties can only be accessed from inside of a class. JavaScript supports class syntax since ES6, but only now were private fields introduced. To define a private property, it has to be prefixed with the hash symbol: #
.
class Flower {
#leaf_color = "green";
constructor(name) {
this.name = name;
}
get_color() {
return this.#leaf_color;
}
}
const orchid = new Flower("orchid");
console.log(orchid.get_color()); // green
console.log(orchid.#leaf_color) // Private name #leaf_color is not defined
If we try to access a private property from outside, an error will be thrown.
To use a class method, a class had to be instantiated first, as shown below.
class Flower {
add_leaves() {
console.log("Adding leaves");
}
}
const rose = new Flower();
rose.add_leaves();
Flower.add_leaves() // TypeError: Flower.add_leaves is not a function
Trying to access a method without instantiating the Flower class would result in an error. Thanks to static fields, a class method can now be declared with the static keyword and called from outside of a class.
class Flower {
constructor(type) {
this.type = type;
}
static create_flower(type) {
return new Flower(type);
}
}
const rose = Flower.create_flower("rose"); // Works fine
So far, to await for a promise to finish, a function in which await is used would need to be defined with the async keyword.
const func = async () => {
const response = await fetch(url)
}
Unfortunately, if there was a need to await for something in a global scope, it would not be possible, and usually required an immediately invoked function expression (IIFE).
(async () => {
const response = await fetch(url)
})()
Thanks to Top Level Await, there is no need for wrapping code in an async function anymore, and this code will work.
const response = await fetch(url)
This feature could be useful for resolving module dependencies or using a fallback source if the initial one failed.
let Vue
try {
Vue = await import('url_1_to_vue')
} catch {
Vue = await import('url_2_to_vue)
}
To wait for multiple promises to finish, Promise.all([promise_1, promise_2]) can be used. The problem is that if one of them fails, then an error will be thrown. Nevertheless, there are cases in which it is ok for one of the promises to fail, and the rest should still resolve. To achieve that, ES11 introduced Promise.allSettled.
promise_1 = Promise.resolve('hello')
primise_2 = new Promise((resolve, reject) => setTimeout(reject, 200, 'problem'))
Promise.allSettled([promise_1, promise_2])
.then(([promise_1_result, promise_2_result]) => {
console.log(promise_1_result) // {status: 'fulfilled', value: 'hello'}
console.log(promise_2_result) // {status: 'rejected', reason: 'problem'}
})
A resolved promise will return an object with status and value properties, while rejected ones will have status and reason.
You might have used dynamic imports when using webpack for module bundling. Finally, native support for this feature is here.
// Alert.js file
export default {
show() {
// Your alert
}
}
// Some other file
import('/components/Alert.js')
.then(Alert => {
Alert.show()
})
Considering the fact that a lot applications use module bundlers like webpack for transpiling and optimizing code, this feature isn't such a big deal right now.
MatchAll is useful for applying the same regular expression to a string if you need to find all matches and get their positions. The match method only returns items that were matched.
const regex = /\b(apple)+\b/;
const fruits = "pear, apple, banana, apple, orange, apple";
for (const match of fruits.match(regex)) {
console.log(match);
}
// Output
//
// 'apple'
// 'apple'
matchAll in contrast, returns a bit more information, including index of the string found.
for (const match of fruits.matchAll(regex)) {
console.log(match);
}
// Output
//
// [
// 'apple',
// 'apple',
// index: 6,
// input: 'pear, apple, banana, apple, orange, apple',
// groups: undefined
// ],
// [
// 'apple',
// 'apple',
// index: 21,
// input: 'pear, apple, banana, apple, orange, apple',
// groups: undefined
// ],
// [
// 'apple',
// 'apple',
// index: 36,
// input: 'pear, apple, banana, apple, orange, apple',
// groups: undefined
// ]
JavaScript can run in different environments like browsers or Node.js. A global object in browsers is available under window variable, but in Node it is an object called global. To make it easier to use a global object no matter in which environment code is running, globalThis was introduced.
// In a browser
window == globalThis // true
// In node.js
global == globalThis // true
The maximum number that can be reliably represented in JavaScript is 2^53 - 1. BigInt will allow creation of numbers even bigger than that.
const theBiggerNumber = 9007199254740991n
const evenBiggerNumber = BigInt(9007199254740991)
I hope you found this article useful and are as excited as I am about the new features that are coming to JavaScript. If you would like to know more about different features you can check the official GitHub repository of the ES committee here.
Thomas Findlay is a 5-star rated mentor, full-stack developer, consultant, technical writer and the author of “React - The Road To Enterprise” and “Vue - The Road To Enterprise.” He works with many different technologies such as JavaScript, Vue, React, React Native, Node.js, Python, PHP and more. Thomas has worked with developers and teams from beginner to advanced and helped them build and scale their applications and products. Check out his Codementor page, and you can also find him on Twitter.