跳到主要内容

ECMAScript 2021 新特性

1. String.prototype.replaceAll

为String增加了新的方法: String.prototype.replaceAll, String.prototype.replace 用于对匹配的字符串进行替换, 但只能替换第一次匹配, 而 replaceAll 可以将所有匹配到的地方全部替换

// 替换首次匹配的1
'1234567123123'.replace('1','t')
// 't234567123123'
// 将所有的1替换为t
'1234567123123'.replaceAll('1','t')
// 't234567t23t23'

String.prototype.replaceAll: 字符串中所有满足 patern 的部分都会被替换

相比于 replace, 不需要使用全局正则表达式了

'hello world'.replaceAll('o', '_'); // hell_ w_rld

'hello world'.replace(/o/g, '_'); // hell_ w_rld

2. Promise.any & AggregateError

Promise.any 接受 Promise 对象数组作为参数,返回成功的 Promise 或者失败的 PromiseAggregateError 类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起

只要给定的参数中的有一个 Promise 成功,就返回第一个 Promise 的值

const pErr = new Promise((resolve, reject) => {
reject("总是失败");
});

const pSlow = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "最终完成");
});

const pFast = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "很快完成");
});

Promise.any([pErr, pSlow, pFast]).then((value) => {
console.log(value);
// pFast fulfils first
});
// 输出: "很快完成"

Promise.any([pErr]).catch((err) => {
console.log(err);
})
// 输出: "AggregateError: No Promise in Promise.any was resolved"

只有当 所有 请求都失败时,我们才最终进入代码 catch 块, 然后在其中处理错误。Promise.any 拒绝可以一次代表多个错误

为了在语言级别支持此功能,引入了一种新的错误类型,称为 AggregateError

除了上面示例中的基本用法外,还可以以编程方式构造 AggregateError 对象,就像其他错误类型一样:

const aggregateError = new AggregateError([errorA, errorB, errorC], 'Stuff went wrong!');

3. WeakRefs 和 FinalizationRegistry 对象

一般来说, 在JavaScript中, 对象的引用是强引用的, 这意味着只要持有对象的引用, 它就不会被垃圾回收

只有当该对象没有任何的强引用时, js引擎垃圾回收器才会销毁该对象并且回收该对象所占的内存空间

WeakRef 提案主要包含两个新功能:

  • 可以通过 WeakRef 类来给某个对象创建一个弱引用

  • 可以通过 FinalizationRegistry 类,在某个对象被垃圾回收之后,执行一些自定义方法

信息

WeakRef 对象包含对对象的弱引用, 这个弱引用被称为该WeakRef对象的target或者是referent

对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为

而与此相反的,一个普通的引用 (默认是强引用) 会将与之对应的对象保存在内存中

只有当该对象没有任何的强引用时, JavaScript引擎GC才会销毁该对象并且回收该对象所占的内存空间

如果上述情况发生了,那么你就无法通过任何的弱引用来获取该对象

一个 WeakRef 对象包含一个对于某个对象的弱引用,通过弱引用一个对象,可以让该对象在没有其它引用的情况下被垃圾回收机制回收

WeakRef 主要用来缓存和映射一些大型对象,当你希望某个对象在不被其它地方引用的情况下及时地被垃圾回收,那么你就可以使用它

危险

注意:正确使用它们需要仔细考虑,如果可能,最好避免使用它们

FinalizationRegistry 接收一个回调函数,返回一个注册器,可以利用该注册器为指定对象注册一个事件监听器,当这个对象被垃圾回收之后,会触发监听的事件

const registry = new FinalizationRegistry((heldValue) => {
// ....
});
// 接着注册一个指定对象,同时也可以给注册器回调传递一些参数:
let theObject;
registry.register(theObject, "some value", theObject);
// 用指定对象取消监听
registry.unregister(theObject);

4. 逻辑赋值运算符

逻辑赋值运算符结合逻辑运算符和赋值运算符,它让代码变得简短、让变量和对象属性条件赋值变得简单

带有 && 运算符的逻辑赋值运算符

let num1 = 5
let num2 = 10
num1 &&= num2

console.log(num1) // 10
// 第5行也可以写成如下方式
// 1. num1 && (num1 = num2)
// 2. if (num1) num1 = num2

带有 || 运算符的逻辑赋值运算符

let num1
let num2 = 10
num1 ||= num2

console.log(num1) // 10
// 第5行也可以写成如下方式
// 1. num1 || (num1 = num2)
// 2. if (!num1) num1 = num2

带有 ?? 运算符的逻辑赋值运算符

let num1
let num2 = 10
num1 ??= num2
console.log(num1) // 10

num1 = false
num1 ??= num2
console.log(num1) // false
// 第3行也可以写成如下方式
// num1 ?? (num1 = num2)

总结

||=  // 结合 OR 逻辑运算符
&&= // 结合 AND 逻辑运算符
??= // 结合空值合并操作符

// 旧
let a = null;
a = a || 'A'; // A
a = a && 'B'; // B
a = a ?? 'C'; // B

// 新
let b = null;
b ??= 'A'; // A
b &&= 'B'; // B
b ||= 'C'; // B

补充:空值合并操作符,只有当左侧的值是 null 或者 undefined 时,才会返回右侧的值

左侧支持 '' 和 0 这样的假值。不能与 AND 或 OR 逻辑运算符共用


5. 数字分隔符

允许使用下划线 ( _, U+005F) 作为分隔符,有助于提高数字文字的可读性

let a = 1_1.1; // 11.1
// 1__1 错误,只允许一个下划线作为数字分隔符
// 1_ 错误,分隔符不能在尾部
// _1 错误,分隔符不能在头部

Number(1_1); // 11
Number('1_1'); // NaN

注意:分隔符不能在尾部和头部,只能在数字中间,只允许一个下划线作为数字分隔符,不可连续

字符串转数值时不支持