EcmaScript 2022中的新特性

EcmaScript 2022中的新特性

ES2022 feature: class static initialization blocks

Everything new coming in ES2022

前言

从2015年起,Javascript每年都会在标准中加入一些新的特性。本篇文章将介绍几个已经到stage 4阶段的标准。一般来说,标准的提案需要经过4个阶段,第4个阶段是最后一个阶段。到达第4阶段的标准也意味着标准制定的工作已经基本结束。

现在我们来一起看一看ES2022中的新特性:

类的字段

类的共有属性和类的私有属性

在这个标准出来之前,我们在class中是这么定义一个属性的:

class ButtonToggle extends HTMLElement {
    constructor(){
        super();
        // public field
        this.color = 'green'
        // private field
        this._value = true;
    }

    toggle(){
        this.value = !this.value
    }
}

const button = new ButtonToggle();
console.log(button.color);
// green - public fields are accessible from outside classes

button._value = false;
console.log(button._value);
// false - no error thrown, we can access it from outside the class

在构造函数中,我们定义了2个属性color和_value。其中一个属性名以_开头,在现有的规范中我们一般这么命名来约定它是一个私有的变量,只能在类的内部对它进行赋值和读值。当然,目前这也仅仅只停留在约定层面,实际上我们在类的外面还是可以正常的操作这个属性的,程序并不会抛出异常。

在ES2022中,我们有一个更容易的方法来强制定义一个属性是共有的还是私有的。下面我们来看一个例子:

class ButtonToggle extends HTMLElement {

    color = 'green';
    #value = true;

    toggle(){
        this.#value = !this.#value;
    }
}
const button = new ButtonToggle();
console.log(button.color);
// green - public fields are accessible from outside classes

// SyntaxError - cannot be accessed or modified from outside the class
console.log(button.#value); 
button.#value = false;

首先,我们不再需要在构造函数中定义和初始化类的属性。其次,我们可以通过#符号来定义一个私有属性。与前面的例子不同的是,当我们在类的外部调用该私有属性时,程序会抛出异常。

类的私有的方法和getter/setters

与上面的例子类似,我们现在可以定义一个私有的方法和getter/setters方法,如下代码所示:

class ButtonToggle extends HTMLElement {

    color = 'green'
    #value = true;

    #toggle(){
        this.#value = !this.#value
    }

    set #setFalseValue(){
        this.#value = false;
    }
}
const button = new ButtonToggle();
// SyntaxError - cannot be accessed or modified from outside the class
button.#toggle();
// SyntaxError - cannot be accessed or modified from outside the class
button.#setFalseValue;

在代码中,我们将toggle方法用#标记变为私有的。这样一来,#toggle方法只能类的内部进行调用,否则也将抛出异常。

静态属性和方法

静态属性和方法这个概念在其它的编程语言中早就已经有了,Javascript也会在2022的标准中加入它。

在过往,静态属性或方法只能通过原型链来调用。如果我们想给类定义一个静态的方法,我们需要在类的外部进行声明,如下代码所示:

class ButtonToggle extends HTMLElement {
    // ... class body
}
ButtonToggle.toggle(){
    // static method define outside of the class body
}

在ES2022中提供了static关键字来允许开发人员在类中声明一个静态属性或方法,可以直接通过类来调用,如下代码所示:

class ButtonToggle extends HTMLElement {

    #value = true;

    static toggle(){
        this.#value = !this.#value
    }
}
// this will work
ButtonToggle.toggle();

// SyntaxError - private static field
const button = new ButtonToggle();
button.toggle();

科学检查私有变量的方式

正如我们在上面的例子中看到的,如果我们尝试访问类之外的私有字段,它将抛出异常并且不会像访问公共字段那样返回 undefined。

我们可以在类中使用try/catch来检查字段是否存在,如下代码所示:

class ButtonToggle extends HTMLElement {

   // initialised as null
    #value = null;

    get #getValue(){
        if(!this.#value){
            throw new Error('no value');
        } 
        return this.#value
    }

    static isButtonToggle(obj){
        try {
            obj.#getValue;
            return true;
        } catch {
            // could be an error internal to the getter
            return false; 
        }
    }

}

在上面的代码中,我们通过在isButtonToggle方法中加上try/catch来捕获当getValue不存在时的异常。但这样实现会在实际应用中遇到一个问题:try/catch中往往还有其它代码逻辑,当异常发生时,开发人员无法判断是由于属性不存在或者仅仅只是其它的异常。因此,ES2022提供了一个更加科学的方式来应对这种情况。可以来看下面的例子:

class ButtonToggle extends HTMLElement {

   // initialised as null
    value = null;

    get #getValue(){
        if(!this.#value){
            throw new Error('no value');
        } 
        return this.#value;
    }

    static isButtonToggle(obj){
       return #value in obj && #getValue in obj
    }

}

类静态代码块

这里其实原文说了很多,我个人觉得弄复杂了。简单来讲,就是可以在类中直接用逻辑初始化静态属性。

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);
    }
  }
}

正则匹配返回下标

可以通过给正则表达式加上 d 字符来让返回的结果中带上匹配字符的开始下标和结束下标。我们来看一组返回的对比:

const fruits = 'Fruits: mango, mangosteen, orange'
const regex = /(mango)/g;

// .exec
RegExp(regex).exec(fruits);
// [
//   'mango',
//   index: 8,
//   input: 'Fruits: mango, mangosteen, orange',
//   groups: undefined
// ]

// matchAll
const matches = [...fruits.matchAll(regex)];
matches[0];
// [
//   'mango',
//   'mango',
//   index: 8,
//   input: 'Fruits: mango, mangosteen, orange',
//   groups: undefined
// ]

vs

const fruits = 'Fruits: mango, mangosteen, orange'
// /gd instead of the previous /g
const regex = /(mango)/gd;

const matches = [...fruits.matchAll(regex)];
matches[0];

// [
// "mango",
// "mango",
// groups: undefined
// index: 8
// indices:[]
//  [8, 13],
//  [8, 13]
// ]
// groups: undefined

可以看到加了 d 字符后,返回结果中多了indices字段,里面包含了命中的下标数组。

在async外部使用await

在这之前,await只能在async方法中使用。在ES2022的标准中,可以在async方法之外使用await。例如,我们可以推迟一个模块及其父模块的执行,直到导入其它内容。

这在一些场景中非常有用,例如你需要引入一个动态路径的资源:

// we need to get the appropriate translation keys based on the language
const translationKeys = await import(`/i18n/${navigator.language}`);

或者想要在一些资源引入失败时,替换成其它资源:

let jQuery;
try {
  jQuery = await import('https://cdn-a.com/jQuery');
} catch {
  jQuery = await import('https://cdn-b.com/jQuery');
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值