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