See the ES2020 standard for full specification of the ECMAScript 2020 language.
ES2020 includes the following new feature proposals:
- Dynamic Imports
BigInt
- Nullish Coalescing Operator
- Optional Chaining Operator
Promise.allSettled()
String.matchAll()
globalThis
import.meta
- Module Namespace Exports
- Standardization of
for-in
order
Dynamic Imports allow to asynchronously import JS files as modules in a native way. The modules are loaded dinamically by using the “function-like”
import()
syntax with a dynamic specifier:import(specifier)
. This behaves in many ways like a function. It returns a promise for the module namespace object of the requested module, created after fetching, instantiating, and evaluating a module’s dependencies, along with the module itself. Thespecifier
will be interpreted the same way as in animport
declaration. While thespecifier
is a string, it is not necessarily a string literal; thus, code likeimport(.`/language-packs/${navigator.language}.js`)
will work.
const baseModulePath = "./baseModules";
const btnImportModule = document.getElementById("btn-import-module");
let userList = [];
btnImportModule.addEventListener("click", async e => {
const userModule = await import(`${baseModulePath}/users.js`);
userList = userModule.getUsers();
});
BigInt
, a new number primitive for working with arbitrary precision integers.BigInt
can represent numbers larger than two to the 53rd power—the largest number JavaScript can represent reliably with theNumber
primitive. ABigInt
is created by appendingn
to the end of the integer or by calling the constructor. This implementation is not backwards compatible because the traditional number system is IEEE754 (which just cannot support numbers of this size).
let reallyBigNumber = 90071992547409920001n;
let positiveBigInt = BigInt(Number.MAX_SAFE_INTEGER); // 9007199254740991n
let negativeBigInt = BigInt(Number.MIN_SAFE_INTEGER); // -9007199254740991n
++reallyBigNumber; // 90071992547409920002n
++positiveBigInt; // 9007199254740992n
--negativeBigInt; // -9007199254740992n
typeof positiveBigInt; // "bigint"
typeof negativeBigInt; // "bigint"
Nullish coalescing is a logical operator—represented by two question marks (
??
)—that adds the ability to truly check nullish values (null
orundefined
) instead of falsey values. In the expressionleftExpression ?? rightExpression
, this operator returnsrightExpression
ifleftExpression
evaluates tonull
orundefined
.
const nonNullishValue = "A non-nullish value";
const nullishValue = null;
const firstExample = nonNullishValue ?? "Fallback value"; // "A non-nullish value"
const secondExample = nullishValue ?? "Fallback value"; // "Fallback value"
A property access and function invocation operator that will short-circuit if the value to access/invoke is nullish. It is a question mark (
?.
) followed by a dot (.
). If the method/function to access/call exists, it is executed. Otherwise, the expression returnsundefined
.
const someObj = {
prop: {
subProp: {
value: "Hello, world!"
}
}
};
const notAFunction = null;
const notAnArray = undefined;
someObj.prop.subProp.value; // "Hello, world!"
someObj.prop.nonExistentProp.value; // Error >> TypeError: Cannot read properties of undefined (reading 'value')
someObj?.prop?.subProp?.value; // "Hello, world!"
someObj?.prop?.nonExistentProp?.value; // undefined
notAFunction(); // Error >> TypeError: notAFunction is not a function
notAFunction?.(); // undefined
notAnArray[0]; // Error >> TypeError: Cannot read properties of undefined (reading '0')
notAnArray?.[0]; // undefined
The
Promise.allSettled()
method is a new Promise combinator that does not short-circuit. It accepts an array of Promises and only resolves when all of them are settled—either resolved or rejected.
// Shows the reason of failure of each rejected promise
const promises = [fetch("/users"), fetch("/roles")];
const allResults = await Promise.allSettled(promises);
const errors = results
.filter(p => p.status === 'rejected')
.map(p => p.reason);
matchAll()
is a new method added to theString
prototype which is related to Regular Expressions. It returns an iterator which returns all matched groups one after another.
const regExp = /[a-c]/g;
const str = "abcde";
let matches = [...str.matchAll(regExp)];
matches.forEach(match => console.log(match));
// [ 'a', index: 0, input: 'abcde', groups: undefined ]
// [ 'b', index: 1, input: 'abcde', groups: undefined ]
// [ 'c', index: 2, input: 'abcde', groups: undefined ]
It provides a standard way of accessing the global
this
object across different JavaScript environments.
globalThis === window; // true
The
import.meta
object contains meta information of the currently imported module. For now, the only available property isurl
: this will either be the URL from which the script was obtained (for external scripts), or the document base URL of the containing document (for inline scripts).
console.log(import.meta); // { url: "file:///home/user/module.js" }
Up until now, two separate statements were needed when exporting a module namespace—first import it and rename it, then export it. This is not the case anymore. Now, we can rename and export in a single line.
// Before
import * as moduleName from "./module.js";
export { moduleName };
// Now
export * as moduleName from "./module.js";
The ECMA specification had not specified the enumeration order in which
for in
loops should iterate. Despite browsers implemented a consistent order on their own before now, this has been officially standardized in ES2020.