1.Class Fields
// 在ES2022之前,给class定义一个字段,我们要在constructor里定义:
class X {
constructor() {
this.a = 123;
}
}
// ES2022允许我们直接这么写:
class X {
a = 123;
}
公共字段和私有字段如果没有被初始化赋值,则会默认设置为undefined。
class X {
a;
#b;
getB() {
console.log(this.#b);
}
}
x = new X();
x.a // undefined
x.getB() // undefined
对于多个class继承,如果一个字段在多个class上都有定义,那么会以最近的class定义为准。(靠近原则)
class A {
a = 1;
}
class B extends A {
a;
}
const b = new B();
b.a // undefined
class C {
a;
}
class D extends C {
a = 1;
}
const d = new D();
d.a // 1
公共字段都是通过Object.defineProperty创造的。当某一个字段,get、set也同时存在时,TC39委员会经过漫长的讨论,最终决定用Object.defineProperty的get、set默认行为,而不是class里定义的get和set。
class A {
set x(value) { console.log(++value); }
get x() { return 'x' }
}
class B extends A {
x = 1;
}
const b = new B();
b.x; // 1 (并不会返回'x')
b.x = 2; // 控制台不会打印3
在ES2022之前,并没有实际意义上的私有字段。大家形成一种默契,通常用下划线_开头的字段名来表示私有字段,但是这些字段还是可以手动更改的。
ES2022给我们提供了更加安全便捷的私有字段定义方法,就是以#开头命名的字段,都会被当成私有字段,在class外部是没办法直接读取、修改这些私有字段的。
class X {
#a = 123;
b = 2;
}
const x = new X();
x.b; // 2
x.#a; // Uncaught SyntaxError: Private field '#a' must be declared in an enclosing class
2.RegExp Match Indices
正则表达式增加了一个/d修饰符,当使用正则表达式的exec()方法时,如果有/d修饰符,那么结果会多返回一个indices属性,用来表示匹配的结果的在原字符串中的起始index值。
const re1 = /a+/d;
const s1 = "aaaabbb";
const m1 = re1.exec(s1);
m1.indices[0] //[0, 3];
如果正则表达式中有具名捕获组,那么indices[1]则表示捕获组的起始index值,indices.groups同样记录了捕获组信息。
const re1 = /a+(?<B>b+)/d;
const s1 = "aaabbbccc";
const m1 = re1.exec(s1);
m1.indices[1] //[3, 6];
m1.indices.groups //{ B: [3, 6] };
3.Top-level await
之前我们使用await时,必须使用async包裹起来,新的提案允许我们直接使用await;
4.Ergonomic brand checks for Private Fields
私有字段检测,之前在判断一个对象里有没有某个私有字段,是比较麻烦的,因为在访问对象上一个不存在的私有属性时,会抛出异常。通常用try/catch确保方法不会报错。
ES2022新提案中,可以用in操作符来判断对象中是否存在某个私有字段、私有方法或者getter。
5. .at()
新增的取值方法,可作用于Array, String, TypedArray。
.at()接收一个参数,对于数组array=[1, 2, 3]
- 当参数是正数n时,结果跟直接获取数组的第n个元素array[n]一样
- 当参数是负数-n时,相当于倒取第n个元素,等同于array[-n + array.length],
- 当参数是其它值或者空时,直接返回数组第一个元素
const arr = [1, 2, 3, 4, 5];
arr.at(1) // 2
arr.at(-1) // 5
arr.at(-10) // undefined
arr.at('aaaa') // 1
arr.at() // 1
6.Error Cause
之前,我们在封装错误信息时,比较繁琐,没有统一的字段的表示错误原因。
新提案在Error构造函数新增了一个可选参数cause,允许我们在实例化Error时,将错误原因以参数形式传入,省去了我们自己单独处理的成本。
async function doJob() {
const rawResource = await fetch('//domain/resource-a')
.catch(err => {
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