Introduction
Hi, DEV community 👋 How're you today?
Let's talk about the modern JavaScript standard → ES2020. Personally, all these new features make me hope that JS will become even more convenient and understandable language, than it is now.
Here we go! 👇
📝 Table of contents
- Breaking news
- Nullish coalescing
- Optional chaining
- Dynamic imports
- Global property
globalThis
- Arbitrary precision integers
BigInt
Promise.allSettled
method
⚡ Breaking news
12 Jun 2020, The ECMA International General Assembly approved specifications, that will be discussed in this article!
- ECMA262 (ECMAScript® 2020 Language Specification)
- ECMA402 (ECMAScript® 2020 Internationalization API Specification)
Congratulations on our new JavaScript! 😄
Nullish coalescing
Proposal: https://github.com/tc39/proposal-nullish-coalescing
In JavaScript there are "false" and "true" values. Now we can say that "zero" values have been added to them as well. Such values include null
and undefined
values.
In JavaScript world, "false" values are:
- empty lines
- number
0
-
undefined
values null
false
NaN
But an expression to check the value to see if it's "zero", will return true
only for null
and undefined
. For example:
const value1 = true;
const value2 = false;
const value3 = undefined;
const value4 = null;
// Function to check values
const getValue = (value) => {
if (value || value === false) return value;
return true;
};
getValue(value1); // => true
getValue(value2); // => false
getValue(value3); // => true
getValue(value4); // => true
But ES2020 standard has enough operator ??
to test for null
and undefined
. You can do it, even without the conditional if
operator!
Just rewrite getValue
function to:
const getValue = (value) => value ?? true;
That's all it takes. Elegant, understandable and professional! 👍
Optional chaining
Proposal: https://github.com/tc39/proposal-optional-chaining
Optional chains allow to organize safe access to deeply nested properties of objects without the need to verify the existence of each of them. Let's take a look at exactly how this feature works.
For example, we have this object:
const user = {
firstName: "John",
lastName: "Doe",
address: {
zip: 100001,
city: "New York",
street: "Long str.",
apartments: 13,
prop1: {
prop2: {
prop3: {
prop4: {
value: 42
}
}
}
}
}
}
And we have the task of getting values from address.street
and ...prop4.value
. Today, we're dealing with it somehow:
if (user && user.address) {
console.log(user.address.street); // => Long str.
}
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); // => 42
}
Huge, ugly and not understandable, right? Okay. Look at the magic, that a new ES2020 standard allows you to do:
console.log(user?.address?.street); // => Long str.
console.log(user?.address?.prop1?.prop2?.prop3?.prop4?.value); // => 42
Is this cool! 🔥 We no longer need massive if...else
conditions. Less code, less bugs and bundle size. But, it's not all! What, if we're make mistake and call unknown_var
?
// Current JS standard:
console.log(user.address.prop1.unknown_var); // => error? undefined? ...?
// ES2020:
console.log(user?.address?.prop1?.unknown_var); // => undefined
Yep, we get a single value of a non-existent variable like undefined
. Always.
Dynamic imports
This technology allows you to import JavaScript files dynamically, as modules, without any additional tools. In this case, if a module is not needed, it is not imported.
Yes, this can be made easier by taking advantage of lazy module loading, for example, by using a code-splitting technology available in Webpack (and not so)... but using it means wasting some system resources. 🤷
Code example for current JS standard:
import { share } from "./share.js"; // => always load module
const shareButton = document.querySelector(".button__share");
shareButton.addEventListener("click", share); // => do something, if clicking
But in ES2020, we've a standard way to dynamically load modules:
const shareButton = document.querySelector(".button__share");
shareButton.addEventListener("click", () => {
import("./share.js") // => load module, only if needed
.then((module) => module.share()) // => do something, if clicking
.catch((error) => console.log(err)); // => handle error
});
Global property globalThis
Proposal: https://github.com/tc39/proposal-global
On the web, global property accessible as window
or self
or this
. On Node.js as global
or this
. And it's not a full list!
The global property globalThis
solve this trouble. It stores a reference to a global object corresponding to the environment where your code is executed.
No more guessing, what to call in code!
Arbitrary precision integers (BigInt)
Proposal: https://github.com/tc39/proposal-bigint
A new BigInt
data type allows you to work with numbers that are longer, than the length of numbers that you could work with in JavaScript before (253).
// Current JS standard:
let n = Number.MAX_SAFE_INTEGER; // => 9007199254740991, this is 1 less than 2^53
const a = n + 1; // => 9007199254740992, ok, checks out
const b = n + 2; // => 9007199254740992, wait, that’s the same as above!
// ES2020:
let n = BigInt(Number.MAX_SAFE_INTEGER); // => 9007199254740991
const c = n + 1n; // => 9007199254740992n
const d = n + 2n; // => 9007199254740993n, this works now!
A BigInt
is created by appending n
to the end of the integer or by calling the constructor.
Promise.allSettled
method
Proposal: https://github.com/tc39/proposal-promise-allSettled
The Promise.allSettled
method will be useful, if you need your code to be executed after you have finished all promises.
Of course, you can use the Promise.all()
method... but it will generate an error, if at least one promise passed to it is rejected. For example:
const array = [
Promise.resolve(100),
Promise.reject(null),
Promise.resolve("Data release"),
Promise.reject(new Error("Something went wrong")),
];
Promise.all(array)
.then((data) => console.log("all resolved! values:", data))
.catch((err) => console.log("got rejected! reason:", err));
// got rejected! reason: null
But with Promise.allSettled
method, promise is successfully resolved only after the work of other promises has been completed. And it doesn't matter, if they have been successfully or unsuccessfully executed.
Promise.allSettled(array)
.then((res) => console.log(res))
.catch((err) => console.log(err));
// [
// { status: "fulfilled", value: 100 },
// { status: "rejected", reason: null },
// { status: "fulfilled", value: "Data release" },
// { status: "rejected", reason: Error: Something went wrong }
// ]
Photo by
[Title] Oskar Yildiz https://unsplash.com/photos/cOkpTiJMGzA
[1] Caspar Camille Rubin https://unsplash.com/photos/0qvBNep1Y04
P.S.
If you want more articles (like this) on this blog, then post a comment below and subscribe to me. Thanks! 😻
❗️ You can support me on Boosty, both on a permanent and on a one-time basis. All proceeds from this way will go to support my OSS projects and will energize me to create new products and articles for the community.
And of course, you can help me make developers' lives even better! Just connect to one of my projects as a contributor. It's easy!
My main projects that need your help (and stars) 👇
- 🔥 gowebly: A next-generation CLI tool that makes it easy to create amazing web applications with Go on the backend, using htmx, hyperscript or Alpine.js and the most popular CSS frameworks on the frontend.
- ✨ create-go-app: Create a new production-ready project with Go backend, frontend and deploy automation by running one CLI command.
Top comments (2)
Dynamic imports are great.. and optional chaining rocks.
Yep! It's very helpful to reduce size of bundle... and looks great 😉