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.