We are closing down to the end of the year, 6 months into the approval of ES2020 specifications - and likely at least 6 months away from ES2021.
Before discussing rumors about next year's release, let's recap what was introduced in ES11, aka ES2020.
You can check how each feature is supported here:
kangax / kangax.github.com
List of my projects and resume
The nullish coalescing operator ??
It's used to provide a default value in place of null and undefined (only).
Fixes the abuse of ||, which defaults on any falsy!
// With ?? the left operand is returned only with null and undefined
null ?? 1 // 1
undefined ?? 1 // 1
false ?? 1 // false
0 ?? 1 // 0
"" ?? 1 // ""
2 ?? 1 // 2
"a" ?? 1 // "a"
true ?? 1 // true
// With || the left operand is returned only with all falsey values
null || 1 // 1
undefined || 1 // 1
false || 1 // 1
0 || 1 // 1
"" || 1 // 1
2 || 1 // 2
"a" || 1 // "a"
true || 1 // true
It's well supported already in browsers and NodeJs (from version 14).
Logical nullish assignment (??=)
The assignment version of the ??
operator is also introduced and supported.
It looks like your regular assignment operators: x ??= y
, which reminds a lot logical assignment operators like ||=
.
x ??= y
, however, only assigns a new value to x
if x
is nullish (null
or undefined
).
Check the difference:
const car = { speed: '150mph' };
car.speed ??= '241.4kmh'; // nothing changes
car.doors ??= 0; // add property doors and set it to 0
car.doors ??= 3 // nothing changes, 0 isn't nullish
car.doors||= 3 // sets cars.doors to 3
As you can see, it's particularly useful for objects, when you aren't sure if a property has been defined already and you don't want to risk overwriting it (but it can also be used with variables, like car ||= {};
).
Optional Chaining ?.
Allows access to nested object properties without worrying if the properties exist or not.
const car = { speed: { value: 150, unit: 'mph'}, doors: 5 };
car.wheels.value // TypeError: Cannot read property 'value' of undefined
car.wheels?.value // undefined
car.speed?.value // 150
car.speed?.whatever // undefined
car.speed?.whatever?.value // undefined
Did you know that it can also be used for function calls?
Like this:
const car = {
speed: {
value: 150,
unit: 'mph'
},
doors: 5,
brake: x => console.log('braking')
};
car.accelerate // TypeError: car.accelerate is not a function
car.accelerate?.() // undefined
car.brake?.() // logs "braking"
The coolest part is, this works so nicely together with the nullish coalescing operator, providing defaults whenever the property chain doesn't exist!
const car = { speed: { value: 150, unit: 'mph'}, doors: 0 };
let wheels = car.wheels?.value ?? 4; // 5
let doors = car.doors?.value ?? 3; // 0, doors is not nullish!
Optional chaining is supported in modern browsers and NodeJs (starting with version 14).
globalThis
A single global object valid and consistent across all JS platforms.
Why is it important? Before ES2020, it was madness when you had to write cross-platform JavaScript referencing the global object.
You had to use:
-
window
on browsers -
global
in NodeJs -
self
for web workers
Now instead, it works like a charm.
This feature is already supported in browsers and, of course, in NodeJs: let's double check...
In Chrome's console (check support here):
In NodeJs (since version 12.0.0):
String.prototype.matchAll
The matchAll
method for strings allows you to iterate through all matched groups of a regular expression.
const regex = /([a-z]+)(\d*)/g;
const txt = "abc1 def ABC WXYZ xyz22 !§ $%& #|";
for (const w of txt.matchAll(regex)) {
console.log(w);
}
With respect to String#match
it allows access to the capture groups, which is particularly convenient to extract info from matched strings! For instance, for an email, you could more easily get username and domain.
Before matchAll
, you still could get the same result, but you would have needed to run a loop where you called RegExp.exec
while (true) {
const match = regex.exec(txt);
if (match === null) {
break;
}
console.log(match);
}
This feature is supported in NodeJs since version 12.0.0, and also now widely supported in browsers.
Promise.allSettled
This new method takes an array of Promises and resolves once all of them are settled, one way or another (either resolved or rejected).
You can hence run a group of promises in parallel, but get a single "exit point" when all of them are completed - in practice allSettled
created a new promise that is fulfilled when all of the original promises are either fulfilled or rejected.
Suppose you have a function that returns a Promise
, something like fetch
, which makes a http call.
We can use it to create an example where we have an array with 2 or more promises, and implement an action that will be performed only when all of these promises are settled:
Promise.allSettled([fetch('http://www.google.com'), fetch('http://www.twitter.com')])
.then(results => {
console.log('All settled', results)
});
(Obviously the array passed to allSettled
can also have promises with a completely different nature and origin, and that can take very different time to settle).
NodeJs supports this method since version 12.9, while you can check out support in browsers here.
Method
Promise.all
was already defined in ECMAScript specification, with a similar behavior and just a small, yet significant, difference: it will reject as soon as any of the promises passed is rejected.
BigInt
Finally JavaScript is introducing arbitrary-precision integers!
Before ES2020 the largest integer that could be represented and stored in JS was 2^53-1
let n = Number.MAX_SAFE_INTEGER; // 9007199254740991
++n // 9007199254740992
++n; // Still 9007199254740992!
Now the limit is your RAM! 😁
n = BigInt(Number.MAX_SAFE_INTEGER); // 9007199254740991n
++n // 9007199254740992n
++n; // 9007199254740993n
Well, at least in theory, since each JS engine needs to put a limit, while implementing it, to the maximum size a BigInt can take - for instance, for V8, it's apparently around 16K (😱) bytes (which is still a lot!).
As you might have noticed, BigInts have a peculiarity, there is an 'n' appended at the end of the number's digits; when you declare a BigInt, to distinguish it from a regular int you also have to add that trailing 'n'. You can highlight the difference by checking the type returned with typeof
.
let m = 9007199254740991n;
let bigInt=1n;
let num = 1;
typeof(bigInt); // 'bigint'
typeof(num); // 'number'
As you can see in the my first example, it's also possible to convert an existing int value or variable:
let num = BigInt(4); // 4n
let m = 42; // 42
num = BigInt(m); // 42n
Be careful, though, because the value to convert must be an integer, you can't pass a floating point to BigInt
constructor:
For the same reason, you cannot mix BigInts and numbers in expressions: while it's kind of obvious for floating points, there is no automatic conversion for integers either:
So, you'll also need to explicitly convert integer values to BigInt:
let n = 4;
let m = BigInt(3n);
n * m // TypeError: Cannot mix BigInt and other types, use explicit conversions
BigInt(n) * m //12n
How is it supported, you might ask: NodeJs supports them since version 10.4.0, check out here for browsers.
Dynamic import
Dynamic import in JavaScript allows you to dynamically import JavaScript modules (or more in general, JavaScript files as modules) in your application. Before ES2020, you could do dynamic import through bundlers; now, this is supported natively.
let mymodule;
if (Math.random() < 0.5) {
// Replace mymodule with a module you have installed!
mymodule = import('mymodule');
}
console.log(mymodule);
If you run this code snippet in node's console, half of the time it will print undefined, and half of the time the result of importing your module. You can leverage this to conditionally load one library or another (which, of course, only makes sense if their public interfaces are compatible, i.e. they expose the same methods... or if you find the right workaround).
For instance, something like:
let jsonModule, jsonParse;
if (condition) {
jsonModule = import('json');
jsonParse = jsonModule.parseJson;
} else {
jsonModule = import('myJson');
jsonParse = jsonModule.parse;
}
let json = jsonParse(jsonText);
Check out which browsers support it here.
import.meta
We only have one last minor feature left to discuss for ES2020, the import.meta
object.
This new property exposes context-specific metadata for a given JavaScript module. These data contain information about the module, specifically, at the moment, the module's URL.
$>node --experimental-modules --es-module-specifier-resolution=node temp.mjs
[Object: null prototype] {
url: 'file://**/temp.mjs'
}
How is it supported? NodeJs supported it since version 10.4, and for what concerns browsers... check it out here.
Top comments (0)