The 5 most transformative JavaScript features from ES12

ES12 was truly an amazing upgrade.

Packed with valuable features that completely transformed the way we write JavaScript.

Code became cleaner, shorter, and easier to write.

Let’s check them out and see the ones you missed.

1. Promise.any()

Before ES12, we already had Promise.all() and Promise.allSettled() to wait for an entire group of Promises.

There were several times when we’d have several Promises but only be interested in whichever one resolved first.

So Promise.any() had to come into the picture:

JavaScript
async function getHelpQuickly() { const response = await Promise.any([ cautiousHelper(), kindHelper(), wickedHelper(), ]); console.log(response); // Of course! } async function cautiousHelper() { await new Promise((resolve) => { setTimeout(() => { resolve('Uum, oohkaay?'); }, 2000); }); } async function kindHelper() { return 'Of course!'; } function wickedHelper() { return Promise.reject('Never, ha ha ha!!!'); } // codingbeautydev.com

One interesting thing to note: even though any() resolves immediately, the app doesn’t end until all the Promises have resolved.

2. replaceAll()

Yes we already had replace() for quickly replace a substring within a string.

JavaScript
const str = 'JavaScript is so terrible, it is unbelievably terrible!!'; const result = str.replace('terrible', 'wonderful'); console.log(result); // JavaScript is so wonderful, it is unbelievably terrible!! // Huh? // codingbeautydev.com

But it only did so for the first occurrence of the substring unless you use a regex.

So ES12 gave us now we have replaceAll() to replace every single instance of that substring.

JavaScript
const str = 'JavaScript is so terrible, it is unbelievably terrible.'; const result = str.replaceAll('terrible', 'wonderful'); console.log(result); // JavaScript is wonderful, it is unbelievably wonderful. // Now you're making sense! // codingbeautydev.com

3. WeakRefs

As from ES12, a JavaScript variable can either be a strong reference or a weak reference.

What are these?

The Strong Refs are our normal everyday variables. But the Weak Refs need to be explicitly created with WeakRef():

JavaScript
const strongRef = { name: 'Tari Ibaba' } const weakRef = new WeakRef(strongRef); // codingbeautydev.com

JS variables are just references to the actual object in memory.

That’s why we can have multiple references to the same object.

JavaScript
const person = { name: 'Tari Ibaba' }; const me = person; // modifies the actual object that `me` points to person.site = 'codingbeautydev.com'; console.log(me.site); // codingbeautydev.com

But what’s the difference between a strong ref and a weak ref?

Well in programming we have something garbage collection, which is when unneeded objects are removed from memory to save resources.

In JavaScript, objects are automatically marked for garbage collected when all the strong ref variables pointing to it have become unreachable — out of scope:

The object person and me both point to is put on the destruction queue once func() runs.

JavaScript
func(); // 💡`person` and `me` are unreachable here function func() { // 💡this object will be marked for garbage collection // after this function runs const person = { name: 'Tari Ibaba' }; const me = person; person.site = 'codingbeautydev.com'; console.log(me.site); // codingbeautydev.com }

But look what happens here:

Even person went out of scope after func() finished, we still had me, a global strong reference.

JavaScript
let me; func(); // 💡one strong reference (`me`) is still reachable // ✅ Can always access object console.log(me.site); function func() { // 💡this object will NOT be garbage collected // after this function runs const person = { name: 'Tari Ibaba' }; me = person; person.site = 'codingbeautydev.com'; console.log(me.site); // codingbeautydev.com }

But what if me was a weak reference?

Now after func() executes, person would be the only strong reference to the object.

So the object will be marked for garbage collection:

JavaScript
let me; func(); // No strong references reachable // ❌ Bad idea: object may not exist console.log(me.deref().site); function func() { // 💡this object will be marked for garbage collection const person = { name: 'Tari Ibaba' }; me = new WeakRef(person); person.site = 'codingbeautydev.com'; console.log(me.deref().site); // codingbeautydev.com }

So why do we need weak references?

The biggest use case for them is caching.

Look what happens here: processData() runs and we have a new object stored in our cache.

Even though data becomes unreachable, the object will never be garbage collected because it has a strong reference in the cache.

JavaScript
let cache = new Map(); processData(); function processData() { const url = 'api.tariibaba.com'; const data = fetchData(url); // process data for app... } async function fetchData(url) { // check cache const saved = cache.get(url); if (!saved) { const data = await (await fetch(url)).json(); cache.set(url, data); } return saved; }

But what if I want the object to be freed up after processData() exits?

I would use a WeakRef as the Map values instead:

JavaScript
let cache = new Map(); processData(); function processData() { const url = 'api.tariibaba.com'; const data = fetchData(url); // process data for app... // 💡the object will only exist in cache // in this function } async function fetchData(url) { // deref weak ref const saved = cache.get(url).deref(); if (!saved) { const data = await (await fetch(url)).json(); // ✅ Use a WeakRef instead cache.set(url, new WeakRef(data)); } return saved; }

4. Logical assignment operators

Lovely syntactic sugar from ES12.

We use them like this:

JavaScript
left ??= right; left ||= right; left &&= right; // codingbeautydev.com

Exactly the same as:

JavaScript
left = (left ?? right); left = (left || right); left = (left && right); // codingbeautydev.com

??=. Quickly assign a value to a variable *if* it is null or undefined (“nullish”).

JavaScript
user.preferredName ??= generateDumbUserName();

||=. Like ??=, but assigns the value for any falsy value (0undefinednull''NaN, or false).

JavaScript
user.profilePicture ||= "/angry-stranger.png";

And then &&=. Something like the reverse; only assigns when the value is truthy (not falsy).

JavaScript
user.favoriteLanguage = await setFavoriteLanguage(input.value); user.favoriteLanguage &&= 'Assembly'; // You're lying! It's Assembly!

5. Numeric separators

Tiny but impactful new addition that made big number literals more readable and human-friendly:

JavaScript
const isItPi = 3.1_415_926_535; const isItAvagadro = 602_214_076_000_000_000_000_000; const isItPlanck = 6.626_070_15e-34; const isItG = 6.674_30e-11; // Works for other number bases too... // codingbeautydev.com

The compiler completely ignores those pesky underscores — they’re all for you, the human!

Final thoughts

These are the juicy new JavaScript features that arrived in the ES12.

Use them to boost your productivity as a developer and write cleaner code with greater conciseness, expressiveness and clarity.



Every Crazy Thing JavaScript Does

A captivating guide to the subtle caveats and lesser-known parts of JavaScript.

Every Crazy Thing JavaScript Does

Sign up and receive a free copy immediately.

Leave a Comment

Your email address will not be published. Required fields are marked *