JavaScript has been developing very fast in recent years. This is especially true for the period following the release of the ES6 standard in 2015. Since then, a lot of great features have appeared in the language. Much new has been proposed for inclusion in the ES2020 standard. A final list of features has already been formed, the appearance of which can be expected in the standard after its approval. This is good news for all JS enthusiasts. The author of the article, the translation of which we are publishing today, says that among these opportunities there are those with which he is especially pleased. Before they appeared, it was much harder for him to write code in situations in which they are applicable. According to him, if they had appeared in the language earlier, they would have saved him a lot of time and effort.
Optional chains
Optional Chaining is, for me personally, one of the most exciting features of the ES2020 standard. I have written many programs in which this feature would be extremely useful. Optional chains allow you to organize secure access to deeply embedded properties of objects without the need to verify the existence of each of them. Take a look at how this feature works.First, look at the code that had to be written before the advent of optional chains.â–Ť Code before the appearance of optional chains
const user = {
firstName:"Joseph",
lastName:"Kuruvilla",
age:38,
address:{
number:"239",
street:"Ludwig Lane",
city:"Chennai",
zip:"600028",
prop1:{
prop2:{
prop3:{
prop4:{
value:'sample'
}
}
}
}
}
}
if(user && user.address){
console.log(user.address.zip);
}
if(user && user.address && user.address.prop1 && user.address.prop1.prop2 && user.address.prop1.prop2.prop3 && user.address.prop1.prop2.prop3.prop4){
console.log(user.address.prop1.prop2.prop3.prop4.value);
}
console.log(user.address.prop102.po);
As you can see, in order to avoid the occurrence of errors, it seems Cannot read property 'po' of undefined
, it is necessary, at each level of nesting, to check the properties for their existence. With an increase in the depth of nesting of entities, the number of checked properties also increases. This means that the programmer himself has to write code that protects it from properties that, when accessed, can run into the values ​​of null
or undefined
.â–Ť Code after the appearance of optional chains
Writing code like the one we just reviewed made it much easier with the advent of optional chains. To organize safe work with deeply embedded properties of objects, it is enough to use the operator ?.
. It saves us from the need to independently check the values ​​on null
and undefined
.Here's what it looks like:const user = {
firstName:"Joseph",
lastName:"Kuruvilla",
age:38,
address:{
number:"239",
street:"Ludwig Lane",
city:"Chennai",
zip:"600028",
prop1:{
prop2:{
prop3:{
prop4:{
value:'sample'
}
}
}
}
}
}
console.log(user?.address?.zip);
console.log(user?.address?.prop1?.prop2?.prop3?.prop4?.value);
console.log(user?.address?.prop102?.po);
Well, isn’t it fine? Thanks to this innovation, ES2020 made it possible to get rid of a huge number of lines of code.Validating only null and undefined values
Checking values ​​only on null
and undefined
(Nullish Coalescing) is one of those features that really delighted me even when the possibilities were at the proposal stage. I often came across the need to write specialized functions to perform the appropriate checks.JavaScript is known to have “false” and “true” meanings. Now we can say that “zero” values ​​were added to them. These values ​​include null
and undefined
. With JavaScript terms of "false" are empty strings, the number 0, the values undefined
, null
, false
, NaN
. That is, for example, a certain expression for checking the value for “falsehood” will work on an empty string, and on the valueundefined
, and much more on what. And the expression for checking the value of whether it is “zero” will return true
only for null
and undefined
. Maybe this opportunity doesn’t seem so wonderful to you personally, but, in fact, it is very, very important.Let's look at some examples.▍ The code until the ability to check values ​​only on null and undefined
Recently, I was working on a project in which I needed to implement the functionality of switching between light and dark themes. At the same time, I had to check the state of the control, find out if it corresponds to the value of true
or false
. If the user did not set any value, by default, it should be equal true
. Here's how I solved this problem before the possibility of checking values ​​only on null
and undefined
:const darkModePreference1 = true
const darkModePreference2 = false
const darkModePreference3 = undefined
const darkModePreference4 = null
const getUserDarkModePreference = (darkModePreference) => {
if (darkModePreference || darkModePreference === false) {
return darkModePreference
}
return true
}
getUserDarkModePreference(darkModePreference1)
getUserDarkModePreference(darkModePreference2)
getUserDarkModePreference(darkModePreference3)
getUserDarkModePreference(darkModePreference4)
▍ Code after the possibility of checking values ​​only on null and undefined
Once this feature has appeared in the language for checking null
and undefined
sufficient operator ??
. You can do without a conditional statement if
:const darkModePreference1 = true
const darkModePreference2 = false
const darkModePreference3 = undefined
const darkModePreference4 = null
const getUserDarkModePreference = (darkModePreference) => {
return darkModePreference ?? true;
}
getUserDarkModePreference(darkModePreference1)
getUserDarkModePreference(darkModePreference2)
getUserDarkModePreference(darkModePreference3)
getUserDarkModePreference(darkModePreference4)
Here the following happens: if the variable darkModePreference
contains a “zero” value, then the value is returned true
. This makes it easier to write code, it is compact and easy to understand.Dynamic imports
Dynamic Imports contribute to more efficient application performance. This technology allows you to import JS files dynamically, in the form of modules, without involving additional tools. Moreover, if the module is not needed, then it is not imported. Prior to the ES2020, modules were imported regardless of whether they are used by the application or not.For example, suppose we need to implement the functionality of loading a certain file in PDF format.Consider, as usual, the old and new options for solving this problem.â–Ť Code before support for dynamic imports
Based on the real situation, we can expect that the ability to download materials in PDF format will not be used by all visitors to the page. But the corresponding module, anyway, needs to be imported into the code. This means that the module, whether it is needed or not, will be loaded when the page loads.import { exportAsPdf } from './export-as-pdf.js'
const exportPdfButton = document.querySelector('.exportPdfBtn');
exportPdfButton.addEventListener('click', exportAsPdf);
This creates an additional load on the system, which can be facilitated by using the lazy loading of modules. This can be done using code separation technology, which is available in webpack or in other module loaders (and their use in itself means spending a certain amount of system resources).But now, thanks to the ES2020, we have a standard way to dynamically load modules, which allows us to do without bundlers.â–Ť Code after the appearance of support for dynamic imports
const exportPdfButton = document.querySelector('.exportPdfBtn');
exportPdfButton.addEventListener('click', () => {
import('./export-as-pdf.js')
.then(module => {
module.exportAsPdf()
})
.catch(err => {
})
})
As you can see, now you can organize lazy loading of modules by loading only when the appropriate module is needed. This reduces system load and increases page loading speed.Construct Promise.allSettled
If you need to perform some action only if all the promises have been successfully resolved, you can use the method Promise.all()
. True, this method has one drawback. The method will throw an error if at least one promise passed to it is rejected. This means that the necessary action will not be performed until all promises are successfully resolved.You may not need this. Perhaps the following scenario will suit you: “The result is not important to me. I need the code to be executed after the completion of all the promises. ” In this case, the method is useful to you Promise.allSettled()
. The corresponding promise is successfully resolved only after the completion of other promises. It doesn’t matter if they have worked successfully or unsuccessfully.▍ Code using the Promise.all construct
const PromiseArray = [
Promise.resolve(100),
Promise.reject(null),
Promise.resolve("Data release"),
Promise.reject(new Error('Something went wrong'))];
Promise.all(PromiseArray)
.then(data => console.log('all resolved! here are the resolve values:', data))
.catch(err => console.log('got rejected! reason:', err))
Apparently, it Promise.all
gives an error after the rejection of one of the promises transferred to it.â–Ť Code that uses the Promise.allSettled construct
const PromiseArray = [
Promise.resolve(100),
Promise.reject(null),
Promise.resolve("Data release"),
Promise.reject(new Error('Something went wrong'))];
Promise.allSettled(PromiseArray).then(res =>{
console.log(res);
}).catch(err => console.log(err));
And here, although some of the promises are rejected, it Promise.allSettled
returns the results issued by all the promises transferred to it.Other notable features
â–Ť BigInt data type
The new data type BigInt
allows you to work with numbers whose length exceeds the length of numbers with which you could work in JavaScript before it appeared ( pow(2,53)-1
). True, this data type is not backward compatible with what was in the language before. The IEEE 754 standard, which is the basis for working with numbers in JavaScript, does not support the numbers that are possible to work withBigInt
â–Ť String.prototype.matchAll method
The method String.prototype.matchAll()
is related to regular expressions. It returns an iterator, allowing you to work with all matches found in a string using a regular expression , including groups .â–Ť Global property globalThis
The global property globalThis
holds a reference to the global object corresponding to the environment in which the code is executed. In the browser, the global object is represented by the object window
. In Node.js, this is an object global
. In web workers, this is an object self
.What innovations do you like most about the ES2020?