The TC39 committee recently approved the ECMAScript 2020 (commonly known as ES2020) candidate which contains the finalized set of features entering the JavaScript language. The ES2020 candidate is set for review and approval by the ECMA general assembly in June this year. Most of the new features are already implemented in browsers and can be transpiled with the Babel JavaScript parser.
The ES2020 specification introduces the matchAll
method attached to Strings
, to produce an iterator for all match objects generated by a global regular expression; import()
, a syntax to asynchronously import modules with a dynamic specifier; BigInt
, for working with arbitrary precision integers; Promise.allSettled
, a new Promise
combinator that does not short-circuit; globalThis
, a universal way to access the global this
value; dedicated export * as ns from 'module'
syntax for use within modules; increased standardization of for-in
enumeration order; import.meta
, a host-populated object available in modules that may contain contextual information about the module; and two new syntax features to improve working with “nullish” values (null
or undefined
): nullish coalescing, a value selection operator; and optional chaining, a property access and function invocation operator that short-circuits if the value to access/invoke is nullish.
Dynamic imports may be used to dynamically load modules generated by the code-splitting phase of bundling applications. Dynamic imports are supported by most browsers with the exception of IE11. With import()
, developers can import a dynamically constructed module (moduleSpecifier = dir + 'my-math.mjs'
) asynchronously (a promise is returned), conditionally or not, at any point of time (e.g. anywhere in the code vs. only at the top-level of a module):
const dir = './lib/';
const moduleSpecifier = dir + 'my-math.mjs';
async function loadConstant() {
const myMath = await import(moduleSpecifier);
const result = myMath.LIGHTSPEED;
assert.equal(result, 299792458);
return result;
}
import.meta
is an object that holds host-specific metadata about the current module. The most interesting property as of today is url
, that is a string with the URL of the current module file. Having that URL allows developers to import related modules, or conditionally perform operations depending on the current module. The proposal provides the following example that fetches a hamster.jpg
file relative to the current module:
(async () => {
const response = await fetch(new URL("../hamsters.jpg", import.meta.url));
const blob = await response.blob();
const size = import.meta.scriptElement.dataset.size || 300;
const image = new Image();
image.src = URL.createObjectURL(blob);
image.width = image.height = size;
document.body.appendChild(image);
})();
When this module is loaded, no matter its location, it will load a sibling file
hamsters.jpg
, and display the image. The size of the image can be configured using the script element used to import it, such as with
<script type="module" src="path/to/hamster-displayer.mjs" data-size="500"></script>
The globalThis
keyword allows developers to write portable ECMAScript code that accesses the global object, eliminating the need to use window
, self
, global
respectively in a browser, worker, Node.js environment, and dealing with other edge cases.
The new export syntax (export * as ns from "mod
) adds a new form of export from
which exports another module’s namespace exotic object as an exported name of another, filling a use case similar to the use cases for existing export from
forms.
Promise.allSettled()
comes to complete the existing ECMAScript ES2015’s Promise.all()
and Promise.race()
, and handles the case when all promises are settled regardless of the result (fulfilled or rejected), allowing to write code as follows:
Promise.allSettled([
fetch("https://api.github.com/users/pawelgrzybek").then(data => data.json()),
fetch("https://api.github.com/users/danjordan").then(data => data.json())
])
.then(result => console.log(`All profile settled`));
With the standard Promise.all()
, a rejection of the first promise would reject the whole promise, and the second promise might not be able to be fulfilled. Promise.allSettled
helps cover such cases where partial results are valuable (one promise rejecting, the other fulfilling).
Optional chaining (a?.b
) and nullish coalescing (a ?? b
) are supported in Babel (since Babel 7.0); Edge, Chrome and Firefox evergreen browsers; and also TypeScript 3.7+. Both features enable short-circuits if the value to access/invoke is nullish. For instance, let x = foo?.bar.baz();
concisely replaces:
let x = foo === null || foo === undefined ? undefined : foo.bar.baz();
BigInt
enables developers to work accurately with very large numbers. A BigInt
is created by appending n
to the end of an integer literal — 10n
— or by calling the function BigInt()
. While 2 ** 53 + 1
would erroneously evaluate to 9007199254740992
(the number is odd, thus cannot end with the digit 2), the following 2n ** 53n +1n
correctly evaluates to 9007199254740993n
. Limitations of these sorts are one reason why Twitter’s JSON API returns Tweet IDs as strings instead of literal numbers. BigInt is supported in most modern browsers (including Firefox 68+, Chrome 67+) and can be worked around in other browsers.
String.matchAll is supported in all evergreen browsers with the exception of Safari. The matchAll()
method returns an iterator of all results matching a string against a regular expression, including capturing groups:
const text = "From 2019.01.29 to 2019.01.30";
const regexp = /(?<year>\d{4}).(?<month>\d{2}).(?<day>\d{2})/gu;
const results = Array.from(text.matchAll(regexp));
// results:
// [
// [
// '2019.01.29',
// '2019',
// '01',
// '29',
// index: 5,
// input: 'From 2019.01.29 to 2019.01.30',
// groups: { year: '2019', month: '01', day: '29' }
// ],
// [ (...) ]
// ]
TC39 is a group of JavaScript developers, implementers, academics, and more, collaborating with the community to maintain and evolve the JavaScript (formally, ECMAScript) specification. TC39 includes all major browser vendors. Each proposal for an ECMAScript feature goes through the following maturity stages: Stage 0: Strawman; Stage 1: Proposal; Stage 2: Draft; Stage 3: Candidate; Stage 4: Finished. A feature will be included in the standard once its proposal has reached stage 4 and thus can be used safely.
The ECMAScript 2020 is the eleventh edition of the ECMAScript Language Specification. Since the publication of the first edition in 1997, ECMAScript has grown to be one of the world’s most widely used general-purpose programming languages.