最全 ECMAScript 攻略之 ES2022-ES13

10,256 阅读5分钟

2022 提案为 ECMAScript Class 新增了下表中所描述的特性(绿色为现有特性)

20210519212706

前置知识

JavaScript设计模式-juejin

在理解 Class 字段特性之前我们需要了解一下面向对象编程的一些概念,话不多说,答案都在上图中

Class 字段相关特性

使用声明式的类字段

从 ES6 Class 开始,我们就可以在 JavaScript 中使用 class 关键字书写传统面向对象编程范式的代码了。

但是在 ES6 版本的 Class 中,我们只能给这个类创建可以公共访问的实例方法和静态方法,如果希望声明类字段的话,只能在 constructor 中直接通过向 this 赋值实现:

class Pokemon {
  constructor() {
    this.name = "Pikachu";
  }
  attack() {}
  static starter() {}
}

这种方式对于工程上实践与代码阅读者来说并不直观。

我们更希望有声明式的类字段,便于代码阅读者了解这个类的数据结构,不需要阅读过程代码就快速地了解这个类的全貌。

在 ES2022 的提案中,我们就可以使用声明式的类字段了

class Pokemon {
  name = "Pikachu";

  attack() {}
  static starter() {}
}

提案所包含的特性目前已经在 Chrome 74Node 12SafariTypeScript 3.8Babel 7.0+ 等等环境中使用

私有属性/私有方法

最新提案之一是在类中添加私有变量的方法。

我们将使用# 符号表示类的私有变量。这样就不需要使用闭包来隐藏不想暴露给外界的私有变量。

通过 # 修饰的成员变量或成员函数就成为了私有变量,如果试图在 Class 外部访问,则会抛出异常。现在,此特性可在最新版本的 Chrome 和 Node.js 中使用。

class Student {
  #name = "something";

  #name2 = "name2";

  // 实例私有方法-只能在此Class内部使用
  #instancePrivateMethod() {
    console.log("实例私有方法");
  }
}

static 字段

它允许类拥有静态字段,类似于大多数 OOP 语言。静态字段可以用来代替枚举,也可以用于私有字段。

class Student {
  // 静态私有字段声明-只能在此Class内部使用
  static #staticFieldPrivateName = "静态私有字段声明:真名-王撕葱";

  // 静态公有字段声明
  static staticFieldPublicName = "静态公有字段声明:名-撕葱";

  // 静态公有方法-ES6已支持
  static staticPublicMethod() {
    console.log("静态公有方法");
  }

  // 静态私有方法-只能在此Class内部使用
  static #staticPrivateMethod() {
    console.log("#staticPrivateMethod:", "静态私有方法");
  }
}

JavaScript 类的私有方法和 getter / setter

在“类”的内部可以使用 getset 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

上面代码中,prop 属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了。

私有属性也可以设置 getter 和 setter 方法

class Student {
  #name = "something";

  #name2 = "name2";

  // 只能在此Class内部使用
  get #privateGet() {
    return this.#name;
  }
  // 只能在此Class内部使用
  set #privateSet(value) {
    this.#name = value;
  }

  get publicGet() {
    return this.#name2;
  }
  set publicSet(value) {
    this.#name2 = value;
  }
}

静态公有/私有字段

场景:当做枚举来使用

class ColorFinder {
  static #red = "#ff0000";
  static #green = "#00ff00";
  static #blue = "#0000ff";

  static colorName(name) {
    switch (name) {
      case "red":
        return ColorFinder.#red;
      case "blue":
        return ColorFinder.#blue;
      case "green":
        return ColorFinder.#green;
      default:
        throw new RangeError("unknown color");
    }
  }

  // Somehow use colorName
}

Class 字段总结

2022js

TypeScript 的对比 2020TS

20210520171518

正则之'/d'修饰符

如今,ECMAScript RegExp 对象可以在调用 exec 方法时提供有关匹配项的信息。

此结果是一个 Array,其中包含有关匹配的子字符串的信息,以及指示输入字符串的其他属性,找到匹配项的输入中的索引以及包含任何已命名捕获组的子字符串的 groups 对象的 groups 对象。

但是,在一些更高级的方案中,此信息可能不一定足够。例如,ECMScript 实现的 TextMate 语言语法高亮显示不仅需要匹配的索引,而且还需要单个捕获组的开始索引和结束索引

因此,ES 规范对 RegExp.prototype.exec()String.prototype.match 等的结果附加了一个 indexs 属性。

indexs 属性本身是一个索引数组,其中包含每个捕获的子字符串的一对开始索引和结束索引

任何不匹配的捕获组都将是不确定的,类似于它们在子字符串数组中的相应元素。另外,索引数组本身将具有一个 groups 属性,其中包含每个命名捕获组的开始索引和结束索引。

const re1 = /a+(?<Z>z)?/d;

// indices are relative to start of the input string:
const s1 = "xaaaz";
const m1 = re1.exec(s1);
m1.indices[0][0] === 1;
m1.indices[0][1] === 5;
s1.slice(...m1.indices[0]) === "aaaz";

m1.indices[1][0] === 4;
m1.indices[1][1] === 5;
s1.slice(...m1.indices[1]) === "z";

m1.indices.groups["Z"][0] === 4;
m1.indices.groups["Z"][1] === 5;
s1.slice(...m1.indices.groups["Z"]) === "z";

// capture groups that are not matched return `undefined`:
const m2 = re1.exec("xaaay");
m2.indices[1] === undefined;
m2.indices.groups["Z"] === undefined;

加上/d 后就会 m1的值就会多出一个 indices属性

顶层 await

在按需加载的场景下, Promise 方式链式调用麻烦的问题

// awaiting.mjs
import { process } from "./some-module.mjs";
const dynamic = import(computedModuleSpecifier);
const data = fetch(url);
export const output = process((await dynamic).default, await data);
// usage.mjs
import { output } from "./awaiting.mjs";
export function outputPlusValue(value) { return output + value }

console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);

参考文档

  1. ECMAScript 双月报告:TC39 2021 年 4 月会议提案进度汇总

最后

文章浅陋,欢迎各位看官评论区留下的你的见解!

觉得有收获的同学欢迎点赞,关注一波!

good

往期文章