The new ES2022 features come early today, considering that some of them are more or less pre-tested.
Finished Proposals have been reached by February 3, 2022, and are in stage 4.
Class Fields
Class Public Instance Fields Public Instance Fields
In an ES6 class, we want to define a default value, which can only be defined by constructor:
class Counter {
constructor() {
this._num = 0; }}Copy the code
Now we can go like this:
class Counter {
_num = 0;
}
Copy the code
Does it feel familiar? Yes, I used it in Node.js V12, but it’s only now coming to the standard.
Of course, you can not initialize, the default is undefined.
Private Instance Fields Private Instance field
All fields of the original class instance can be accessed and modified:
class Counter {
_num = 0;
}
const counter = new Counter();
console.log(counter._num); / / 0
Copy the code
_num is a convention for private usage, but does not prevent it from being accessed or modified.
Private can now be denoted with the # prefix, which throws an error when we access or modify:
class Counter {
#num = 0;
}
const counter = new Counter();
console.log(counter.#num); // Uncaught SyntaxError: Private field '#num' must be declared in an enclosing class
Copy the code
Private instance methods and accessors
In addition to private fields, methods and accessors can also be used to indicate private with the # prefix:
class Counter {
#num;
constructor() {
console.log(this.#getNum) // undefined
this.#initNum = 0;
console.log(this.#getNum) / / 0
}
get #getNum() {
return this.#num
}
set #initNum(num) {
this.#num = num; }}const counter = new Counter();
console.log(counter.#initNum); // VM723:1 Uncaught SyntaxError: Private field '#initNum' must be declared in an enclosing class
Copy the code
Static class Fields and methods Static public fields and methods
In the new proposal, we can add static fields and methods to the class using the static keyword declaration, which is very common in other languages:
class Counter {
#num = 0;
static baseNum = 100;
Static methods can access static fields through this
static getDoubleBaseNum() {
return this.baseNum * 2; }}// Static fields and methods are accessed through the class itself
console.log(Counter.baseNum); / / 100
console.log(Counter.getDoubleBaseNum()); / / 200
// Instances cannot access static fields and methods
const counter = new Counter();
console.log(counter.baseNum); // undefined
Copy the code
Private Static Class Fields and Methods Static Private fields and methods
Static fields and methods can also be private with a # prefix:
class Counter {
#num = 0;
static #baseNum = 100;
static getDoubleBaseNum() {
return this.#baseNum * 2;
}
getBaseNum() {
returnCounter.#baseNum; }}Private static fields cannot be accessed directly
console.log(Counter.#baseNum); // Uncaught SyntaxError: Private field '#baseNum' must be declared in an enclosing class
Private static fields can be accessed by static methods of the same kind
console.log(Counter.getDoubleBaseNum()); / / 200
// Instances can access private static fields and methods of the same class
const counter = new Counter();
console.log(counter.getBaseNum()); / / 100
Copy the code
Class Static Block Static initialization Block
This proposal is also relatively familiar, Java language is useful, let’s look at an example:
class Counter {
static running;
static {
try {
this.running = doRun();
} catch {
this.running = false; }}}Copy the code
From the above we can see that static {} looks like static constructor.
It also has access to modify private static fields and methods:
class Counter {
static #baseNum = 100;
static getDoubleBaseNum() {
return this.#baseNum * 2;
}
static {
this.#baseNum = 200; }}console.log(Counter.getDoubleBaseNum()); / / 400
Copy the code
Even expose private static fields:
let getBaseNum
class Counter {
static #baseNum = 100;
static {
getBaseNum = () = > this.#baseNum; }}console.log(getBaseNum()); / / 100
Copy the code
Ergonomic brand checks for Private Fields
Check whether an object or instance has private fields or methods:
class C {
#brand;
#method() {}
get #getter() {}
static isC(obj) {
return #brand in obj && #method in obj && #getter inobj; }}Copy the code
RegExp Match Indices
The new proposal allows us to use the/D identifier to represent the start and end indexes of the string we want to match. Here’s an example:
const re1 = /a+(z)? /;
const s1 = "xaaaz";
const m1 = re1.exec(s1);
console.log(m1[0]); // 'aaaz'
console.log(m1[1]); // 'z'
Copy the code
Indices: /d: /d: /d: /d: /d:
const re1 = /a+(z)? /d;
const s1 = "xaaaz";
const m1 = re1.exec(s1);
console.log(m1.indices[0]); / / [1, 5]
console.log(s1.slice(... m1.indices[0])); // 'aaaz'
console.log(m1.indices[1]); / / [4, 5]
console.log(s1.slice(... m1.indices[1])); // 'z'
Copy the code
You can also add named groups, such as? The < Z > :
const re1 = /a+(?
z)? /
d;
const s1 = "xaaaz";
const m1 = re1.exec(s1);
console.log(m1.groups.Z); // 'z'
console.log(m1.indices.groups.Z); / / [4, 5]
Copy the code
Top-level await
This proposal allows “await” to be promoted to modules without strong binding with async. Prior to this:
// awaiting.mjs
let output;
async function main() {
output = await new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(100)},500)}); } main();export { output };
Copy the code
If we want to reference output:
// usage.mjs
import { output } from "./awaiting.mjs";
console.log(output); // undefined
setTimeout(() = > console.log(output, 1000); / / 100
Copy the code
Obviously, we can’t reference an asynchronous value immediately, so in most cases we’ll fix the problem by referring to a method that returns an asynchronous call, as in:
// compiler.mjs
let vueCompiler
const getVueCompiler = async() = > {if (vueCompiler) return vueCompiler
vueCompiler = await import('@vue/compiler-sfc')
return vueCompiler
}
export { getVueCompiler };
// usage.mjs
import { getVueCompiler } from "./compiler.mjs";
const compiler = await getVueCompiler()
Copy the code
With top-level await, we can:
// awaiting.mjs
function main() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(100)},500)}); }export const output = await main();
// usage.mjs
import { output } from "./awaiting.mjs";
console.log(output); / / 100
setTimeout(() = > console.log(output, 1000); / / 100
Copy the code
As you can see, await does not need to be used in async functions, we reference modules can wait for ESM modules to finish asynchronously before executing.
.at()
To access an item in an array, we usually do this:
const arr = [1.2.3]
console.log(arr[0]); / / 1
console.log(arr[arr.length - 1]); / / 3
Copy the code
As you can see from the above, it is ok to access the previous array, but it is slightly uncomfortable to access the reverse array, especially when the array is dynamically computed, for example:
console.log([1.2.3 ...].map(v= > v + 1) [[1.2.3 ...].map(v= > v + 1).length - 1]); / / 4
Copy the code
In this case, we have to store it in a variable.
Array.prototype.at (string.prototype. at);
const arr = [1.2.3]
console.log(arr[arr.length - 1]); / / 3
/ / left left
console.log(arr.at(-1)); / / 3
// Dynamic calculation can also be simplified
console.log([1.2.3 ...].map(v= > v + 1) [[1.2.3 ...].map(v= > v + 1).length - 1]); / / 4
/ / left left
console.log([1.2.3 ...].map(v= > v + 1).at(-1)); / / 4
Copy the code
Accessible Object.prototype.hasOwnProperty
I remember the earliest days, when we were going to iterate over an object, we would say something like this:
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
// Get enumerable objects}}Copy the code
Subsequent use of ESLint in this way will prompt you:
Do not access Object.prototype method 'hasOwnProperty' from target object.
Copy the code
This is an unsafe behavior, such as {“hasOwnProperty”: 1}, that can cause the server to crash.
To solve the problem, let’s change it to this:
Object.prototype.hasOwnProperty.call(obj, 'key')
Copy the code
This avoids accessing the target Object prototype method.
Getting to the point, the new proposal simplifies:
Object.prototype.hasOwnProperty.call(obj, 'key')
/ / left left
Object.hasOwn(obj, 'key')
Copy the code
Error Cause
Here’s an example:
async function getSolution() {
const rawResource = await fetch('//domain/resource-a')
.catch(err= > {
// There are several ways to throw an error:
// 1. throw new Error('Download raw resource failed: ' + err.message);
// 2. const wrapErr = new Error('Download raw resource failed');
// wrapErr.cause = err;
// throw wrapErr;
// 3. class CustomError extends Error {
// constructor(msg, cause) {
// super(msg);
// this.cause = cause;
/ /}
/ /}
// throw new CustomError('Download raw resource failed', err);
})
const jobResult = doComputationalHeavyJob(rawResource);
await fetch('//domain/upload', { method: 'POST'.body: jobResult });
}
await doJob(); // => TypeError: Failed to fetch
Copy the code
In the new proposal, cause is added to collect the cause, standardizing the entire error throw and collection:
async function doJob() {
const rawResource = await fetch('//domain/resource-a')
.catch(err= > {
// Throw a low-level Error "err", which can be wrapped as a higher-level Error by cause
throw new Error('Download raw resource failed', { cause: err });
});
const jobResult = doComputationalHeavyJob(rawResource);
await fetch('//domain/upload', { method: 'POST'.body: jobResult })
.catch(err= > {
throw new Error('Upload job result failed', { cause: err });
});
}
try {
await doJob();
} catch (e) {
console.log(e);
console.log('Caused by', e.cause);
}
// Error: Upload job result failed
// Caused by TypeError: Failed to fetch
Copy the code
With this, the various error classes in the various plug-in libraries can be simplified.
The resources
TC39 ECMA262 flip to the bottom.
collection
- New features of ES2022(ES13
- New features of ES2021(ES12
- New features for ES2020(ES11)
- Nine new features that ES2019(ES10) brings
- ES2018 (ES9) new features
- ES2017 (ES8) new features
- ES2016 (ES7) new features