Exciting Features in ES2022

March 28, 2022

In this article we will look at some cool features that are coming in ECMAScript 2022. Since its inception in 1997, ES2022 will be Javascript's 13th edition. At the time of writing this article all the features have already reached Stage 4 and are expected to be released by July, 2022.

1. Class Fields

Private Fields and Methods

Previously there wasn't any mechanism to setup private class fields in Javascript.

class Post {
  title = "Default Title"; // public
  _date = Date.now(); // private
 
  print() {
    console.log(this.title, this.date);
  }
}
 
const post = new Post();
post.title = "New Post"; // public field accessed from outsite
 
post._date = "2022/10/20"; // even though we have access private field outsite of class
 
post.print();

Even though _date field is marked as private, _ underscore is just naming convention for private field, it's not there in language specification so can be accessed from outside and doesn't shows any error. So there wasn't any Encapsulation in fields and methods in Js specs.

Now in ES2022 we have private class fields and methos. For that we have to prefix our methods and fields with pound # sign. Also, whenever we try to access private field we have to prefix #.

class Post {
  title = "Default Title";
  #date = Date.now(); // private field from ES2022
 
  print() {
    console.log(this.title, this.#date); // ✅ can access private field within class
  }
 
  #getTitle() {
    return this.title;
  }
}
 
const post = new Post();
post.title = "New Post";
 
post.#date = "2022/10/20"; // ❌ Uncaught SyntaxError: Private field '#date' must be declared in an enclosing class
post.getTitle(); // ❌ Uncaught SyntaxError: Private field '#print' must be declared in an enclosing class
post.print();

Static Methods and Fields

The static members of a class are accessed using the class name and dot notation, without creating an object.

Previously,

class Cirlce {
  getArea() {}
}
 
Cricle.PI = 3.14; // define sttaic fields

But now with introduction to static keywordwe can directly define it inside class.

class Circle {
  static PI = 3.14;
 
  static getArea(radius) {
    return this.PI * Math.pow(radius, 2);
  }
}
 
console.log(Circle.getArea(3));

2. Top-level await

Previously we need we can't declare await keyword outside async function.

When we fetch the data from api using async await, we return promises and use then() to use the data.

async function getPost() {
  const data = await fetch("https://jsonplaceholder.typicode.com/posts/1");
  const postJson = await data.json();
  return postJson;
}
 
getPost().then((post) => console.log(post));

But now we can directly use await keyword without wrapping it inside async function. With top-level await it will treat the whole module as async function.

const post = await fetch("https://jsonplaceholder.typicode.com/posts/1");
console.log(posts);

There we other exciting usecases for it Check tc39 proposal.

3. Relative indexing using .at() method

Till noew we can only access the elements in array using positive index, so have calculate the length of array and calculate the value accordingly. After ES2022, we can easily access the elements using positive or negative index using at() method.

const arr = Array(100)
  .fill()
  .map(() => Math.random());
 
arr[arr.length - 1]; // prev, 100
 
arr.at(-1); // last num i.e 100
arr.at(-2); // second last i.e 99

4. Accessible Object.hasOwn() method

This method is used to confirm wether the certian property exists in object itself or nested prototypical chain of that object. Previously we used to have Object.hasOwnProperty() to do the same thing. hasOwnProperty tries to check if certain property exists inside object itself, if that property exists inside its prototype then it will return false. Issue with this method is that, JavaScript does not protect the property name hasOwnProperty; an object that has a property with this name may return incorrect results:

const user = {
  name: "John",
 
  // override hasOwnProperty() method in obj
  hasOwnProperty() {
    return false;
  },
};
 
user.hasOwnProperty("name"); // always return false since we override it to return false

Instead of calling hasOwnProperty() on object prototypical chain (user in our case), we pass the object and property we want to check inside the hasOwn() method shipped in ES2022 Object class itself.

const user = {
  name: "John",
  age: 20,
};
 
Object.hasOwn(user, "name"); // true

5. Error .cause

Most of the time error are wrapped with meaningful message about case of error, but original contextual information of error might get lost. Using error cause we can attach the original error to a wrapping error. The options parameter is added to Error() constructor with cause property and a cause field is used for retrieving the original error.

const getPost = async (postId) => {
  try {
    const data = await fetch(
      `https://jsonplaceholder.typicode.com/posts/${postId}`,
    );
    const postJson = await data.json();
    return postJson;
  } catch (error) {
    throw new Error(`Loading data for post with id ${postId} failed.`, {
      cause: error,
    });
  }
};
try {
  const post = await getPost(4352);
  // do something
} catch (error) {
  console.log(error); // Error: Loading data for post with id 4352 failed.
  console.log(error.cause); // TypeError: Failed to fetch
}

6. RegExp Match Indices

RegExp Match Indices provide additional information about the start and end indices of captured substrings. While calling the RegExp.exec or String.matchAll() methods, we provide additional /d flag to regular expression, the start and end index are available in the indices array property of the result.

In the below example we find all the indices of the switch reserved keyword present in out code.

const statement = "const switch = 0";
const regex = /(switch)/dg;
 
const matches = [...statement.matchAll(regex)];
 
console.log(matches);
 
/**
 * Result :
[
  "switch",
  "switch",
  groups: undefined,
  index: 6,
  indices: [
    [6, 12],
    [6, 12],
    groups: undefined
  ]
  input: "const switch = 0",
  length: 2,
]
 */

Most of the above features are already available in modern browsers, and Node.js environment, but for production use we might need to polyfill for target environment.

References