Soon the new version of ECMA Script will become standard in few months. So let's take a glimpse at the features that will be part of ES2022.
Features of ES2022
1. Method .at()
of indexable values.
This function let's us reads an element at the given index. It can accept negative indexes to read elements from the end of the given datatype.
For example
[1,2,3,4,5].at(3) // returns 4
[1,2,3,4,5].at(-2) // returns 4
Datatypes supporting this function.
- String
- Array
- All Typed Array classes: Uint8Array etc.
2. RegExp match indices
Adding a flag /d
to a regular expression produces match objects that records the start and end of each group capture.
There are different ways to match indices
- Match indices for numbered group
const matchObj = /(a+)(b+)/d.exec('aaaabb');
console.log(matchObj);
/*
Output -
['aaaabb', 'aaaa', 'bb', index: 0, input: 'aaaabb', groups: undefined, indices: Array(3)]
*/
Due to the regular expression flag /d
, matchObj also has a property .indices that records for each numbered group where it was captured in the input string.
matchObj.indices[1];
/*
Output -
[0, 4]
*/
- Match indices for named groups
const matchObj = /(?<as>a+)(?<bs>b+)/d.exec('aaaabb');
console.log(matchObj);
/*
Output -
['aaaabb', 'aaaa', 'bb', index: 0, input: 'aaaabb', groups: {as: 'aaaa', bs: 'bb'}, indices: Array(3)]
*/
Their indices are stored in matchObj.indices.groups
matchObj.indices.groups;
/*
Output -
{ as: [0,4], bs: [4,6] }
*/
3. Object.hasOwn(obj, propKey)
It is a safe way to check that propKey
is the own property of obj
object. It is similar to Object.prototype.hasOwnProperty
but it supports all object types.
const proto = {
protoProp: 'protoProp',
};
const obj = {
__proto__: proto,
objProp: 'objProp',
};
console.log('protoProp' in obj); // output - true.
console.log(Object.hasOwn(obj, 'protoProp')) // output - false
console.log(Object.hasOwn(proto, 'protoProp')); // output - true.
4. error.cause
Error and it's subclasses now let us specify the reason behind the error. This is useful in deeply nested function where we have chained error blocks to quickly find the error. Read here for more info
function readFiles(filePaths) {
return filePaths.map(
(filePath) => {
try {
// ยทยทยท
} catch (error) {
throw new Error(
`While processing ${filePath}`,
{cause: error}
);
}
});
}
5. Top-level await modules
We can now use await at the top levels of modules and donโt have to enter async functions or methods anymore.
- Loading modules dynamically
const messages = await import(`./messages-${language}.mjs`);
- Using a fallback if module loading fails
let lodash;
try {
lodash = await import('https://primary.example.com/lodash');
} catch {
lodash = await import('https://secondary.example.com/lodash');
}
- Using whichever resource loads fastest
const resource = await Promise.any([
fetch('http://example.com/first.txt')
.then(response => response.text()),
fetch('http://example.com/second.txt')
.then(response => response.text()),
]);
6. New members of classes
-
Public properties can be created via
- Instance public fields
class InstPublicClass {
// Instance public field
instancePublicField = 0; // (A)
constructor(value) {
// We donโt need to mention .property elsewhere!
this.property = value; // (B)
}
}
const inst = new InstPublicClass('constrArg');
- Static public fields
const computedFieldKey = Symbol('computedFieldKey');
class StaticPublicFieldClass {
static identifierFieldKey = 1;
static 'quoted field key' = 2;
static [computedFieldKey] = 3;
}
console.log(StaticPublicFieldClass.identifierFieldKey) //output -> 1
console.log(StaticPublicFieldClass['quoted field key']) //output -> 2
console.log(StaticPublicFieldClass[computedFieldKey]) //output -> 3
-
Private slots are new and can be created via
- Instance private fields
class InstPrivateClass {
#privateField1 = 'private field 1'; // (A)
#privateField2; // (B) required!
constructor(value) {
this.#privateField2 = value; // (C)
}
/**
* Private fields are not accessible outside the class body.
*/
checkPrivateValues() {
console.log(this.#privateField1); // output -> 'private field 1'
console.log(this.#privateField2); // output -> 'constructor argument'
}
}
const inst = new InstPrivateClass('constructor argument');
inst.checkPrivateValues();
console.log("inst", Object.keys(inst).length === 0) //output -> inst, true
- Instance and static private fields
class InstPrivateClass {
#privateField1 = 'private field 1'; // (A)
#privateField2; // (B) required!
static #staticPrivateField = 'hello';
constructor(value) {
this.#privateField2 = value; // (C)
}
/**
* Private fields are not accessible outside the class body.
*/
checkPrivateValues() {
console.log(this.#privateField1); // output -> 'private field 1'
console.log(this.#privateField2); // output -> 'constructor argument'
}
static #twice() {
return this.#staticPrivateField + " " + this.#staticPrivateField;
}
static getResultTwice() {
return this.#twice()
}
}
const inst = new InstPrivateClass('constructor argument');
inst.checkPrivateValues();
console.log("inst", Object.keys(inst).length === 0) //output -> "inst", true
console.log(InstPrivateClass.getResultTwice()); // output -> "hello hello"
- Private methods and accessors
class MyClass {
#privateMethod() {}
static check() {
const inst = new MyClass();
console.log(#privateMethod in inst) // output-> true
console.log(#privateMethod in MyClass.prototype) // output-> false
console.log(#privateMethod in MyClass) // output-> false
}
}
MyClass.check();
- Static initialisation blocks in classes. For static data we have Static fields and Static Blocks that are executed when the class is created.
class Translator {
static translations = {
yes: 'ja',
no: 'nein',
maybe: 'vielleicht',
};
static englishWords = [];
static germanWords = [];
static { // (A)
for (const [english, german] of Object.entries(this.translations)) {
this.englishWords.push(english);
this.germanWords.push(german);
}
}
}
console.log(Translator.englishWords, Translator.germanWords)
//Output -> ["yes", "no", "maybe"], ["ja", "nein", "vielleicht"]
- Private slot checks - This functionality helps us to check that the object has the given private slot in it.
class C1 {
#priv() {}
static check(obj) {
return #priv in obj;
}
}
console.log(C1.check(new C1())) // output true
These amazing features will help us to enhance our projects and improve our coding techniques. I am really excited to try these features out in my project. ๐
Happy Coding! ๐ฉ๐ปโ๐ป
Top comments (75)
I will miss arr[arr.Length -2]
True ๐
... or not! ๐
I didn't like this one. It could mislead me sometimes ๐/
Don't worry, you can still use it if you want ;)
hhhhhhhhhhhhhhhhh
so true hahahahah
Ohh wow
Thanks๐
What is
error.cause
for?Normally, the cause of the error is what I would put in the error message. I mean, what else would you put there?
Why do we need another field for that?
I don't know of any other language that has two text messages in the same exception. What for? ๐คทโโ๏ธ
The error message might sometimes be used for a public-facing message. You could then put more technical information in the cause as well. That's my guess, at least.
Ah, mystery solved!
2ality.com/2021/06/error-cause.html
It's for chaining errors - like the "inner exception" you have in many other languages.
Because you can throw anything in JS, the property type isn't specified as Error - but the intention is similar.
Thanks for attaching this link for reference. ๐
The way this would be super helpful is in deeply nested functions with chained errors.
Like if we have mutiple try catch block inside a deeply nested function so we pass the string as the purpose of that block and cause as the argument of catch block. For example:
That sounds unlikely?
At least, I've never heard of anyone using exceptions for display purposes - exceptions generally do contain technical information, they are usually intended for developers, not for users. How would you deal with localization, for instance... That's just not what exceptions are for...
I may be wrong but I do not think this is correct "For static data we have Static fields and Static Blocks that are executed every time a new instance is created.".
Static blocks are evaluated once during Class definition not every time a new instance is created.
When a new instance is created, it is the code in the construction that is executed.
Yes you are right, static blocks are executed when a class is created not in case of instance. Updated the statement.
Thanks๐
Informative post, thank you. There is apparently an error in the output listed for
matchObj.indices.groups
however which should instead by an object keyed by group.Hey,
Actually, I included the full output which is an array having an object keyed by group.
I shouldโve included just the groups output for more clarity.
Thanks for pointing out buddy!๐๐
This is what I see as the output showing on the page:
There's no object showing there...
I was referring to the snippet just above this one ๐
I shouldโve returned the full object instead of returning just the indices.
Updated the post. Thanks ๐
Top-level await is what I've been waiting for forever!!
Can't wait for these to come out!!
flatMap operator is alternative but sure its nice to have
I'm very excited for being able to use the top level await examples you gave (minus the lodash part).
๐
Can you give an example as of why?
/ C# dude here
superb post, thanks.
๐
Thank you for sharing the post. :)
๐
Thanks for the breakdown. Nicely written too ๐
Thanks๐