DEV Community

Cover image for 7 Features of ES2022 You Should Know
Jollen Moyani for Syncfusion, Inc.

Posted on • Originally published at syncfusion.com on

7 Features of ES2022 You Should Know

JavaScript is evolving continuously, and each year the latest edition of the ECMAScript specification is released with new features and changes. ES2022 is the 13th edition of JavaScript, released in June 2022.

As a developer, you should be up to date on these new features, as they help you improve the quality of your code. Let’s look at the most important features that reached the fourth stage of the TC39 process and were published in the June 2022 release.

1. RegExp match indices

By using regular expressions, we can search for patterns in strings. Regexp.exec and String.matchAll are two methods used in JavaScript to retrieve a list of matches. Both return the matched substrings; the initial input string; the index in the input where the match was found; and the group’s object, consisting of substrings for any named capture groups. Refer to the following example.

const animals = "Animals: Brown Bear, Koala, Polar Bear"
// without /d flag
const regex = /(Bear)/g
console.log(...animals.matchAll(regex))
// Output:
// [
// 'Bear',
// 'Bear',
// index: 15,
// input: "Animals: Brown Bear, Koala, Polar Bear",
// groups: undefined
// ] [
// 'Bear',
// 'Bear',
// index: 34,
// input: "Animals: Brown Bear, Koala, Polar Bear",
// groups: undefined
// ]
Enter fullscreen mode Exit fullscreen mode

There may be situations where you need more than just the index of the match but also the start and end indices for each captured substring. With this new JavaScript feature, you can use the character d to express that you want both starting and ending indices of the specified matched string. Refer to the following example.

const animals = "Animals: Brown Bear, Koala, Polar Bear"
// with /d flag
const regex2 = /(Bear)/dg
console.log(...animals.matchAll(regex2))
// Output:
// [
// 'Bear',
// 'Bear',
// index: 15,
// input: "Animals: Brown Bear, Koala, Polar Bear",
// groups: undefined,
// indices: [[15, 19], [15, 19], groups: undefined, length: 2 ]
// ] [
// 'Bear',
// 'Bear',
// index: 34,
// input: "Animals: Brown Bear, Koala, Polar Bear",
// groups: undefined,
// indices: [[34, 38], [34, 38], groups: undefined, length: 2 ]// ]
Enter fullscreen mode Exit fullscreen mode

As you can see, when the new /d flag is used, it returns the start and end positions (indices) of each matched group capture.

2. Top-level await

Although the asynchronous functions have been used in JavaScript for some time now, before ES2022, await was only available in the scope of async. In ES2022, you can use await outside the context of an async method and provide a solution for the synchronization issues with which you have previously dealt. For example, await holds up the rendering of a module and its parents until the resources are imported and ensures that things are properly coordinated.

There are several use cases for top-level await :

  • Allows modules to use runtime values to determine dependencies.
// Appropriate translation keys are needed based on the language.
const translationKeys = await import(/api/${language} );

Enter fullscreen mode Exit fullscreen mode
  • Resource initialization.
const connection = await dbConnector();
Enter fullscreen mode Exit fullscreen mode
  • Dependency fallbacks.
let URL;
try {
  url = await import('https://cdn-a.com/jQuery');
} catch {
  url = await import('https://cdn-b.com/jQuery');
}
Enter fullscreen mode Exit fullscreen mode

3. .at() for indexing

Before ES2022, square bracket notation was used to fetch a particular element in an array. This method is straightforward unless you need to perform a backward iteration, i.e., negative indexing. In the case of negative indexing, the common practice was to use arr[arr.length — N], where array length is referred to and indexed from the end. Unfortunately, this method requires naming the indexable twice and adding an additional seven characters to the .length. It is also hostile to anonymous values; you need first to store the return value of a function in a temp variable to get the last item.

arr.slice(-N)[0] is another method that avoids the drawbacks of the arr[arr.length — N] method but also has its own performance drawbacks.

The .at() method introduced in ES2022 has simplified this process. In a case of positive indexing, .at() will work the same as the square brackets. But for negative indexing, the .at() method allows starting the iteration from the end.

[a,b,c,d,e].at(3) //Output: d
[a,b,c,d,e].at(-2) //Output: d
Enter fullscreen mode Exit fullscreen mode

Data types supported by .at() are string, array, and all typed array classes: Int8Array, Uint8Array, etc.

4. Accessible Object.prototype.hasOwnProperty

Object.hasOwn() is a static method that you can use to check if a property exists in an object or not. It returns true if the specified object contains the indicated property as its own, and if the property is inherited or doesn’t exist, it returns false. This method takes the object as the first argument and the property you want to check as the second.

Object.hasOwn is the intended alternative for the Object.prototype.hasOwnProperty method. Although Object.prototype.hasOwnProperty has been in JavaScript specification for quite a time, it has some drawbacks.

**hasOwnProperty** does not work for objects created using Object.create(null) as it does not inherit from Object.prototype , which makes the hasOwnProperty unreachable for the created object. Using hasOwnProperty , the object will throw an exception.

hasOwnProperty() can be overridden, as it is not a reserved JavaScript object method.

Due to these two raised problems, it is recommended to use Object.hasOwn over the object.prototype.hasOwnProperty(). Although it is possible to work around the mentioned issues by calling hasOwnProperty() on an external object, hasOwn() is more intuitive.

const book = {
  name: "Harry Potter",
  author: "J.K. Rowling",
 }

// Using Object.prototype.hasOwnProperty() method
console.log(book.hasOwnProperty("name")) //Output: true
console.log(book.hasOwnProperty("price")) //Output: false

// Using Object.hasOwn method
console.log(Object.hasOwn(book, "name")) //Output: true

// Issues with hasOwnProperty

// Issue 01: Doesn't work for objects created using Object.create(null)const person = Object.create(null)
person.name = "Mike"console.log(person.hasOwnProperty("name")) //Output: TypeError: object2.hasOwnProperty is not a function
console.log(Object.hasOwn(person, "name")) //Output: true

// Issue 02: hasOwnProperty can be overridden
const author = {
  name: "J.K. Rowling",
  hasOwnProperty() {
    return false
  },
}  

console.log(author.hasOwnProperty("name")) //Output: false
console.log(Object.hasOwn(author, "hasOwnProperty")) //Output: true

Enter fullscreen mode Exit fullscreen mode

5. Error cause

Errors are raised to identify runtime abnormalities. However, if the error is thrown from a deep internal method, identifying the reason behind it may not be straightforward without a proper exception design pattern.

The .cause property of the error object allows you to specify the reason behind the unexpected exceptions. This is a perfect way to chain the errors together and is very helpful in deeply nested functions, as the errors can be chained without using over-elaborated formalities on wrapping the errors in conditions.

try {  
  const result = 2 / 0;  
  console.log(result);
} catch (error) {  
    console.err(error);  
    throw new Error('Something went wrong', { cause: error });
  }
Enter fullscreen mode Exit fullscreen mode

6. Class

Public and private instance fields

class User {
  constructor(){
      Public fieldthis.userName = 'Hermione Granger'
      Private fieldthis._password = 'abc123';
  }
}

const user = new User();
console.log(user.userName); // Hermione Granger - public fields are accessible outside the class

console.log(user._password); // abc123 - no error thrown, can access the private field from outside the class
Enter fullscreen mode Exit fullscreen mode

Refer to the previous example. As you can see, before ES2022, you had to invoke the constructor to define both public and private fields. Moreover, although the Private keyword was used to declare the field as private, when we try to access it outside the class, it doesn’t throw any errors. This is because private is only a naming convention used to define private fields and not exactly something that JavaScript itself enforces.

With ES2022, you don’t have to define fields inside a constructor anymore. Also, you can define a private field by inserting # as a prefix of the field name. This avoids accessing private fields outside the class and throws an error if you try to do so.

class User {
  userName = 'Hermione Granger'
  #password = 'abc123';
}

const user = new User();
console.log(user.userName); // Hermione Granger - public fields are accessible outside the class

console.log(user.#password); // Uncaught SyntaxError: Private field '#password' must be declared in an enclosing class
Enter fullscreen mode Exit fullscreen mode

Static class fields and private static methods

Before ES2022, static fields and methods could not be accessed in an instance of a class, only in the prototype, like in the following code.

class User {
  ...
}

User.userName = "Hermione Granger";
Enter fullscreen mode Exit fullscreen mode

This feature of ES2022 lets you define them directly inside the class body using the keyword static.

class User {
  static userName = "Hermione Granger"
}

User.userName;
Enter fullscreen mode Exit fullscreen mode

Similarly, you can combine the static keyword with # to create a private static method that is only accessible from inside the prototype class.

class User {
  #userName;

  static #login() {this.#userName = "Hermione Granger";
}

User.#login() // Returns an error
Enter fullscreen mode Exit fullscreen mode

7. Ergonomic brand checks for private fields

If you try to access a private field of an object that has not been declared, it will throw an exception and not an undefined error like it usually does with public fields.

You can use a try/catch inside the class to check if a private field exists, like in the next example.

class UserLogin {

   // Initialized as null#userName = null;

    get #getUser(){if(!this.#userName){throw new Error('No Data!');
      } 
      return this.#userName
    }

    static isLogin(user){
        try {
            obj.#getUser;return true;
        } catch {
            return false; 
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The issue is that you don’t know whether the catch block is executed because the getter is not present or because it threw an error.

The latest **in** operator introduced in ES2022 provides an easy way to overcome this issue. By using it, you will be able to check whether a field is present in a specific class or not. Let’s adjust our previous example.

class UserLogin {
   // Initialized as null#userName = null;
    get #getUser(){if(!this.#userName){throw new Error('No Data!');
        }
        return this.#userName;
    }

    static isLogin(user){
        return #userName in user && #getUser in user
    }
}

Enter fullscreen mode Exit fullscreen mode

The isLogin will check the presence of private fields in the class.

Conclusion

In this article, I discussed the latest ES2022 JavaScript features that would help you to improve your coding skills. I hope you will use these new features during your development process to make your code more updated.

I hope you have found this article helpful. Thank you for reading!

The Syncfusion JavaScript suite is the only suite you will ever need to build an application. It contains over 65 high-performance, lightweight, modular, and responsive UI components in a single package. Download the free trial and evaluate the controls today.

If you have any questions or comments, you can contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!

Related blogs

Top comments (0)