DEV Community

loading...
Cover image for ES2021 Features! 🔥

ES2021 Features! 🔥

KUMAR HARSH
The best way to learn is to teach. Programmer by Passion and Developer for Fun, and I love sharing my journey with everyone.
Updated on ・6 min read

This article focuses on the JavaScript capabilities that are a part of ECMAScript 2021(ES12).

  • 1. Logical Assignment Operators (&&= ||= ??=)
  • 2. Numeric Separators (1_000)
  • 3. Promise.any & AggregateError
  • 4. String.prototype.replaceAll
  • 5. WeakRefs & FinalizationRegistry Objects

Now let's look into each one in detail.

Logical Assignment Operators

Logical assignment operator combines the logical operations(&&, || or ??) with assignment expressions.

//"Or Or Equals"
x ||= y;
x || (x = y);

// "And And Equals"
x &&= y;
x && (x = y);

// "QQ Equals"
x ??= y;
x ?? (x = y);
Enter fullscreen mode Exit fullscreen mode

Logical assignment operator with &&

let x = 1;
let y = 2;
x &&= y;
console.log(x); // 2
Enter fullscreen mode Exit fullscreen mode

x &&= y can be expanded to x && (x = y)

Or in other way, it is like:

if(x) {
  x = y
}
Enter fullscreen mode Exit fullscreen mode

Since x is a truthy value, it is assigned with the value of y, i.e 2.

Logical assignment operator with ||

Here is the code.

let x = 1;
let y = 2;
x ||= y;
console.log(x); // 1
Enter fullscreen mode Exit fullscreen mode

Here x ||= y can be expanded like x || (x = y).

That means the assignment operation happens only if x is a falsy value. In our code, x contains 1 which is a truthy value, and hence, the assignment does not happen. That is why our code prints 1 in the console.

In short:

const updateID = user => {

  // We can do this
  if (!user.id) user.id = 1

  // Or this
  user.id = user.id || 1

  // Or use logical assignment operator.
  user.id ||= 1
}
Enter fullscreen mode Exit fullscreen mode

Logical assignment operator with ??

?? in JavaScript specifically checks if a value is null or undefined.

let a;
let b = a ?? 5;
console.log(b); // 5
Enter fullscreen mode Exit fullscreen mode

In line 2 let b = a ?? 5, if the value of a is null or undefined, the right-hand side of ?? is evaluated and assigned to b.

Let us now consider ?? along with =.

let x;
let y = 2;
x ??= y;
console.log(x); // 2
Enter fullscreen mode Exit fullscreen mode

x ??= y in the above code is equivalent to x = x ?? (x = y).

Here the value of x is undefined. So the right-hand side expression is evaluated and sets x to 2.

Numeric Separators

It allows us to add an underscore(_) character between numbers to make them more readable.

For instance:
const num = 100000000

Confused by the number of Zeroes 🤯.

Separators solve this problem:
const num = 100_000_000

Much more readable and easy to understand right.

Separators can be used in both the integer and decimal parts of a number.

const num = 1_000_000.123_456

Separators can be used not only in integers and floating-point numbers but also in binary, hexadecimal, octal literals.

The underscore(_) separator also works with BigInt numbers.

const trillion = 1000_000_000_000n;
console.log(trillion.toString()); // "1000000000000"
Enter fullscreen mode Exit fullscreen mode

The separator is just for readability purposes. So, it can be placed anywhere within the number.

const amount = 178_00; // 00 after _ for cents.
Enter fullscreen mode Exit fullscreen mode

Promise.any and AggregateError

Promise.any() returns the value of the first fulfilled promise. If all promises passed to Promise.any() as an argument (as an array) are rejected, an "AggregateError" exception is thrown.

AggregateError is a new Error subclass that groups individual errors. Each AggregateError instance contains a reference to an array with exceptions.

Let's consider an example:

Below we have 3 promises, which resolve at random times.

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("A"), Math.floor(Math.random() * 1000));
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("B"), Math.floor(Math.random() * 1000));
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("C"), Math.floor(Math.random() * 1000));
});
Enter fullscreen mode Exit fullscreen mode

Out of p1, p2 and p3, whichever resolves first is taken by Promise.any().

(async function() {
  const result = await Promise.any([p1, p2, p3]);
  console.log(result); // Prints "A", "B" or "C"
})();
Enter fullscreen mode Exit fullscreen mode

What if none of the promises resolve? In that case Promise.any() throws an AggregateError exception. We need to catch it and handle it.

const p = new Promise((resolve, reject) => reject());

try {
  (async function() {
    const result = await Promise.any([p]);
    console.log(result);
  })();
} catch(error) {
  console.log(error.errors);
}
Enter fullscreen mode Exit fullscreen mode

For demo purposes, only one promise is passed to Promise.any(). And that promise is rejected. The above code logs the following error in the console.

error

String.prototype.replaceAll() Method

String.prototype.replaceAll() allows you to replace all instances of a substring in a string with a different value without using a global regex.

Currently, the JavaScript string has a replace() method. It can be used to replace a string with another string.

const str = "Backbencher sits at the Back";
const newStr = str.replace("Back", "Front");
console.log(newStr); // "Frontbencher sits at the Back"
Enter fullscreen mode Exit fullscreen mode

If the input pattern is a string, replace() method only replaces the first occurrence. That is why in the code, the second occurrence of "Back" is not replaced.

We can do a full replacement only if we supply the pattern as a regular expression.

const str = "Backbencher sits at the Back";
const newStr = str.replace(/Back/g, "Front");
console.log(newStr); // "Frontbencher sits at the Front"
Enter fullscreen mode Exit fullscreen mode

String.prototype.replaceAll() is trying to bring the full replacement option even when the input pattern is a string.

const str = "Backbencher sits at the Back";
const newStr = str.replaceAll("Back", "Front");
console.log(newStr); // "Frontbencher sits at the Front"
Enter fullscreen mode Exit fullscreen mode

Let's see another example:

In the following example, we replace all "+" characters with commas with a space using a regular expression:

const strWithPlus = '++'
const strWithComma = strWithPlus.replace(/+/g, ', ')
// , , 
Enter fullscreen mode Exit fullscreen mode

This approach requires the use of a regular expression. However, complex regular expressions are often a source of errors. (Nobody likes RegEx 😬)

There is another approach based on using the String.prototype.split() and Array.prototype.join() methods:

const strWithPlus = '++'
const strWithComma = strWithPlus.split('+').join(', ')
// , , 
Enter fullscreen mode Exit fullscreen mode

This approach avoids using regular expressions, but you have to split the string into separate parts (words), convert it to an array, and then concatenate the array elements into a new string.

String.prototype.replaceAll() solves these problems and provides a simple and convenient way to globally replace substrings:

const strWithPlus = '++'
const strWithComma = strWithPlus.replaceAll('+', ', ')
// , ,
Enter fullscreen mode Exit fullscreen mode

NOTE: If a global regular expression is used as the lookup value, then replace and replaceAll behave the same.

WeakRefs and FinalizationRegistry Objects

WeakRef stands for Weak References. The main use of weak references is to implement caches or mappings to large objects. In such scenarios, we do not want to keep a lot of memory for a long time saving this rarely used cache or mappings. We can allow the memory to be garbage collected soon and later if we need it again, we can generate a fresh cache.

JavaScript is a garbage-collected language. If a variable is no longer reachable, the JavaScript garbage collector automatically removes it. You can read more on JavaScript garbage collection here in MDN.

WeakRefs(weak references) provides two new features:

  • Creating Weak References to an Object Using the WeakRef Class
  • Running custom finalizers after garbage collection using the FinalizationRegistry class

In short, WeakRef allows you to create weak references to objects that are the values ​​of properties of another object, and finalizers can be used, among other things, to remove references to objects "cleaned up" by the garbage collector.

This technique can be useful when creating memorization (memoization) function that uses the built-in cache to prevent repeated execution of the function if there is a calculated value for the argument passed to the function in the cache (provided that objects are used as values ​​for the properties of the cache object and the risk of their subsequent deletion)

When it comes to building an inline cache:

  • If there is no risk of memory leaks, use Map

  • When using key objects that can be subsequently deleted, use WeakMap

  • When using value objects that can be subsequently deleted, use Map in conjunction with WeakRef

An example of the last case from the proposal:

function makeWeakCached(f) {
  const cache = new Map()
  return key => {
    const ref = cache.get(key)
    if (ref) {
      //     
      const cached = ref.deref()
      if (cached !== undefined) return cached;
    }

    const fresh = f(key)
    //    ( )
    cache.set(key, new WeakRef(fresh))
    return fresh
  };
}

const getImageCached = makeWeakCached(getImage);
Enter fullscreen mode Exit fullscreen mode
  • The WeakRef constructor takes an argument that must be an object and returns a weak reference to it
  • The deref method of a WeakRef instance returns one of two values.

In the case of a built-in cache, the finalizer is designed to complete the cleanup process after a value object is destroyed by the garbage collector, or, more simply, to remove a weak reference to such an object.

function makeWeakCached(f) {
  const cache = new Map()
  //    -   
  const cleanup = new FinalizationRegistry(key => {
    const ref = cache.get(key)
    if (ref && !ref.deref()) cache.delete(key)
  })

  return key => {
    const ref = cache.get(key)
    if (ref) {
      const cached = ref.deref()
      if (cached !== undefined) return cached
    }

    const fresh = f(key)
    cache.set(key, new WeakRef(fresh))
    //      ( )
    cleanup.register(fresh, key)
    return fresh
  }
}

const getImageCached = makeWeakCached(getImage);
Enter fullscreen mode Exit fullscreen mode

Read more about finalizers and how to use them in the proposal. In general, finalizers should only be used when necessary.

If you liked my content consider following me on Twitter

Also if you got any questions feel free to ping me on Twitter.

References: 1 2 3

Thank You! 😊

Discussion (51)

Collapse
hasnaindev profile image
Muhammad Hasnain

String.replaceAll is kind of useless, something nobody asked for. Regular Expression in context of simple find and replace is very very basic and easy. Also, non of these are great features TBH. We'll now spend the next five years telling developers not to use them because we want to support older browsers.

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

I am happy with the _ 😂, If possible I would include them in C++ and Java as well.

Collapse
robcodes profile image
RobCodes

You'll love ruby. We have had most of these operators / separators a while now.

:)

Thread Thread
hasnaindev profile image
Muhammad Hasnain

Isn't Ruby, like dead? I used it a little when I had to write some Shopify script though.

Thread Thread
robcodes profile image
RobCodes

I can say that 100% Ruby is not dead. don't believe the hype. The community ist growing as fast as it used to, but its still growing. RoR is now a mature framework, and sr. ruby developers still make a pretty hefty salary. Sure, there are other up and comers, and I dabble in them. But ruby still pays my bills. I am an avid go-lang writer, and i use it when its right to do so. But I will bet you dollars to donuts, I can build you a working MVP in 1/10th the time, and 10x the stability with ruby and rails.

I do love Node, and react. and I think thats a great stack as well. But ruby will always be my first love.

Thread Thread
hasnaindev profile image
Muhammad Hasnain • Edited

As far as I know, I heard that people aren't making a lot of new projects in Ruby, they are still making but the number is getting lower and most of the ruby developers usually maintain legacy code. Sad, I never got to experience Ruby though, Ruby developers seems to love the heck out of it.

Thread Thread
Sloan, the sloth mascot
Comment deleted
kevinluo201 profile image
LuoKevin

This site, dev.to, is built by ROR

Thread Thread
robcodes profile image
RobCodes

Exactly.

As I said. Ruby is great. And it has a great community. I enjoy my work. there are rare occasions where I like something in another language.

It has to do with the scope I work in. Im not building ATC systems. I'm not building medical devices. Im not building radar systems. I build Web Apps. :)

cenacr007_harsh profile image
KUMAR HARSH Author

Maybe in the future.

Collapse
ienzam profile image
Md. Enzam Hossain • Edited

C++ and Java both have this feature already.

In C++, you can use ' (single quote) as digit separator - 1'000'000

In Java, you can use _ as digit separator - 1_000_000

Thread Thread
cenacr007_harsh profile image
KUMAR HARSH Author

For real ?? 😳 All this time of using C++ and I never knew about this 🥲

Collapse
chawan4u profile image
mahesh

nice

Collapse
hasnaindev profile image
Muhammad Hasnain

Ooh yes, I didn't pay much attention to that. This is definitely a nice feature!

Thread Thread
cenacr007_harsh profile image
KUMAR HARSH Author

Sure is.

Collapse
stojakovic99 profile image
Nikola Stojaković

For years it's been possible to use new features from the JS even in older browsers by using Babel.

Collapse
hasnaindev profile image
Muhammad Hasnain

True but then you increase the bundle size with all the polyfills. Honestly, I could careless but these are the things "senior" developers have told me and it turns out that there are as many opinions as there are "senior" developers.

I guess I'm just being sarcastic but it is a fact that software development industry with its "senior" developers with five years of experience has made it very toxic. Everyone thinks their opinions are facts. I have a lot to say about this, maybe I'll write a post instead someday.

Thanks.

Thread Thread
stojakovic99 profile image
Nikola Stojaković

In software development, everything is about balance. You don't need to use all the features all the time. Also, compilers are much smarter now, with tree shaking and all other optimizations available to us we can have very small bundles.

There are rather few things which could be proven in software development too - most of it boils down to opinions. That's the both, a burden and a blessing.

Thread Thread
hasnaindev profile image
Muhammad Hasnain • Edited

FYI, I know these terms and the concept you are talking about are quite primitive like tree shaking etc. I also understand that software engineering is very different compared to other engineering disciplines because most of it is based on abstractions. We don't have to understand how the machine works but using those abstractions we can create beautiful things.

My only problem with the development community is toxic "senior" developers that are in over their head, trying to micro-manage and enforce their opinions. Read a book or talk to an actual senior developer with 20 or 30 years of experience and you'll understand that a lot of "senior" developers that I talk about throw terms around and enforce their opinions in shallow hopes of appearing smart.

Thread Thread
cenacr007_harsh profile image
KUMAR HARSH Author

Yup that throwing terms around, I have seen that a lot.

Thread Thread
stojakovic99 profile image
Nikola Stojaković

Even better if you know them already. I typically listen to people who are in this industry more than me because most of what I've read is useful. The only exception would be developers who are working industry for a long time at the same position. They typically don't learn anything new and they're just piling up years for the CV. Throwing terms around is sometimes I notice fairly often from people who aren't in this industry or are junior developers.

Not having opinions would make software development unbearable job. I have my opinions about structuring my projects, writing tests, documenting things and so on which were of course influenced by the industry standards, community and so on. We can all agree that there is no single right way to do things.

Thread Thread
cenacr007_harsh profile image
KUMAR HARSH Author

Yeah one must have opinions, just we should never force it on others.

Collapse
intrnl profile image
intrnl • Edited

That's really subjective, and it would of course depends on what kind of code you're dealing to begin with.

Personally, I've found logical assignment and numeric separators to be useful for me already. AggregateError and WeakRef seems more useful in the library-land, where you won't really be peeking much to begin with.

I don't really like supporting older browsers older than one-two years 🤷‍♂️️

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

True that, I am very happy IE is finally getting retired, like why the hell was it there in the first place, just to make our life harder.

Collapse
blackr1234 profile image
blackr1234

Regarding browser compatibility concern, perhaps developers can use Babel to polyfill?

Collapse
dfedotov profile image
Dmitry Fedotov

Hello, Kumar,
Thanks for your article.
Please don't use "var" when writing about ES2021 features :)

Collapse
rishitkhandelwal profile image
Rishit Khandelwal

var is different than let, they have different properties in execution.

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

true that 😂

Collapse
dfedotov profile image
Dmitry Fedotov

Thanks for the change! It's important for people who are new to the language to avoid the var misuse.

Thread Thread
cenacr007_harsh profile image
KUMAR HARSH Author

Yup.

Collapse
rishitkhandelwal profile image
Rishit Khandelwal

I love regex

Collapse
ashoutinthevoid profile image
Full Name

Same here. The 'nobody likes regex' line was a tableflip moment.

The only thing i dont like about regex are the subtly different flavors. If my project has multiple languages (say ts and rust), my terminal , and nvim, I have to remember and context switch between multiple flavors of regex. 😵

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

I guess I'd like it too if I understand it better 😅

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

They are useful indeed.

Collapse
simlrh profile image
simlrh

x &&= y can be expanded to x && (x = y)

I can see this is true, but it really hurts my brain. I always thought of it as x = x && y!

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

Yup we used to that shorthand notation for a long time.

Collapse
rohitk570 profile image
ROHIT KUMAR

Nice article 👍

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

Thanks.

Collapse
chawan4u profile image
mahesh

Nice

Collapse
hemanth profile image
hemanth.hm
Collapse
nemethricsi profile image
Richard

I like regex 😅

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

Yup many do.

Collapse
williamvu1999 profile image
William-Vu-1999

very nice

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

Thanks.

Collapse
givehug profile image
Vladimir Boretskyi

Im still waiting for the day when the start removing features :) (or bugs, or useless syntax)

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

Yup, there is a limit to backward compatibility, it's been ages now, it's about damn time we saw some changes.

Collapse
rash123 profile image
RASHMI VERMA

Great 👌👌

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

Thanks.

Collapse
vivekku36049297 profile image
Vivek kumar singh

Love from rungta college

Collapse
cenacr007_harsh profile image
KUMAR HARSH Author

INDEED