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:
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.
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.
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()
:
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.
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.
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.
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:
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.
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:
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:
left ??= right;
left ||= right;
left &&= right;
// codingbeautydev.com
Exactly the same as:
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”).
user.preferredName ??= generateDumbUserName();
||=
. Like ??=
, but assigns the value for any falsy value (0
, undefined
, null
, ''
, NaN
, or false
).
user.profilePicture ||= "/angry-stranger.png";
And then &&=
. Something like the reverse; only assigns when the value is truthy (not falsy).
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:
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.
11 Amazing New JavaScript Features in ES13
This guide will bring you up to speed with all the latest features added in ECMAScript 13. These powerful new features will modernize your JavaScript with shorter and more expressive code.