简述
ES2021(ES12)将于 2021 年中发布。在本文中,你将将会了解五个最有趣的功能:String.prototype.replaceAll()
,数字分隔符,逻辑赋值运算符,Promise.any()
,WeakRef
和Finalizers。
本文所描述的五个功能目前都处于第 4 阶段。这意味着它们已经完成,并将要在 JavaScript 引擎中实现了。这意味着你不会浪费时间去学习一些可能永远也不会出现的东西。
这些功能不久将会发布。如果有兴趣,可以到官方 Ecma TC39 GitHub (https://github.com/tc39/proposals) 去了解有关其他提案的更多信息。这个 Github 库跟踪了所有提案以及其当前所处的阶段。
String.prototype.replaceAll()
先从一个小功能 replaceAll()
开始,这是对 JavaScript 语言的一个补充。当你要替换字符串中多次出现的匹配模式时,目前可以用 replace()
方法,但问题是它只能替换第一次出现的那个。
这并不意味着 replace()
不能替换所有出现的匹配模式,只不过你必须用正则表达式才行。如果你可以接受那就没事儿了。不过对于很多 js 程序员来说,正则表达式并不是他们的菜(实际上是懒得学!)。
如果你就是这样的 js 程序员,肯定喜欢新的 replaceAll()
方法。它的工作方式与 replace()
类似,区别在于 replaceAll()
可以不用正则表达式就能替换所有出现的模式。
replaceAll()
也能接受正则表达式,你完全可以用它代替 replace()
。
// 声明一个字符串
let str = 'There are those who like cats, there those who like watching cats and there are those who have cats.'
// 用 dogs 替换所有的“cats”:
str = str.replaceAll('cats', 'dogs')
console.log(str)
// Output:
// 'There are those who like dogs, there those who like watching dogs and there are those who have dogs.'
// 用 replace() 的写法:
str = str.replace(/cats/g, 'dogs')
console.log(str)
// Output:
// 'There are those who like dogs, there those who like watching dogs and there are those have dogs.'
数字分隔符
这是 JavaScript ES2021的一个非常小的功能,可以让你在处理大量数字时更好过一点。数字分隔符提供了一种能使大数字更易于阅读和使用的简单方法。语法也很简单:一个下划线 _
。
// 不带数字分隔符的 Number
const num = 3685134689
// 带数字分隔符的 Number
const num = 3_685_134_689
不过数字分隔符只是在视觉上提供一些帮助。在使用时不会对数值本身产生任何影响。
// 带数字分隔符的 Number
const num = 3_685_134_689
// 输出:
console.log(num)
// Output:
// 3685134689
逻辑赋值运算符
JavaScript 允许在布尔上下文中使用逻辑运算符。例如在 if ... else
语句和三目运算符中检测是 true
还是 false
。ES2021 的逻辑赋值运算符将逻辑运算符与赋值表达式(=
)组合在了一起。
在 JavaScript 中已经有了一些赋值运算符,例如:加法赋值(+=
),减法赋值(-=
),乘法赋值(*=
)等。在 ES2021 中又增加了对逻辑运算符 &&
,||
和 ??
([空值合并)的支持。
//////////////////
// 逻辑与赋值运算符 (&&=)
//////////////////
x &&= y
// 以上代码相当于:
x = x && d
// 或者:
if (x) {
x = y
}
// 例1:
let x = 3
let y = 0
x &&= y
console.log(x)
// Output:
// 0
// 例 2:
let x = 0
let y = 9
x &&= y
console.log(x)
// Output:
// 0
// 例 3:
let x = 3 // Truthy value.
let y = 15 // Truthy value.
x &&= y
console.log(x)
// Output:
// 15
//////////////////
// 逻辑或赋值运算符 (||=):
x ||= y
// 相当于:
x = x || y
// 或者:
if (!x) {
x = y
}
// 例 1:
let x = 3
let y = 0
x ||= y
console.log(x)
// Output:
// 3
// 例 2:
let x = 0
let y = 9
x ||= y
console.log(x)
// Output:
// 9
// 例 3:
let x = 3
let y = 15
x ||= y
console.log(x)
// Output:
// 3
/////////////////////////
// 空值合并赋值运算符 (??=):
/////////////////////////
x ??= y
// 相当于:
x = x ?? y
// 或者:
if (x == null || x == undefined) {
x = y
}
// 例 1:
let x = null
let y = 'Hello'
x ??= y
console.log(x)
// Output:
// 'Hello'
// 例 2:
let x = 'Jay'
let y = 'Hello'
x ??= y
console.log(x)
// Output:
// 'Jay'
// 例 3:
let x = 'Jay'
let y = null
x ??= y
console.log(x)
// Output:
// 'Jay'
// 例 4:
let x = undefined
let y = 'Jock'
x ??= y
console.log(x)
// Output:
// 'Jock'
看一下上面的例子。首先是 x && = y
。仅当 x
为真时,才将 y
赋值给 x
。其次是 x || = y
,仅当 x
为假时,才将 y
赋值给 x
。如果 x
是真,而 y
是假,则不会进行赋值。
如果 x
和 y
都是假,也会发生同样的情况。最后是 x ?? = y
。仅当 x
为 null
或undefined
时,才将 y
分配给 x
。如果 x
既不是 null
也不是 undefined
则不会进行赋值,如果 y
为 null
或 undefined
也一样。
Promise.any()
在 ES6 中引入了 Promise.race()
和 Promise.all()
方法,ES2020 加入了 Promise.allSettled()
。ES2021 又增加了一个使 Promise 处理更加容易的方法:Promise.any()
。
Promise.any()
方法接受多个 promise,并在完成其中任何一个的情况下返回 promise。其返回的是Promise.any()
完成的第一个 promise。如果所有 promise 均被拒绝,则 Promise.any()
将返回 AggregateError
,其中包含拒绝的原因。
// 例 1: 全部被完成:
// 创建 promises:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise1 is resolved.')
}, Math.floor(Math.random() * 1000))
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise2 is resolved.')
}, Math.floor(Math.random() * 1000))
})
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise3 is resolved.')
}, Math.floor(Math.random() * 1000))
})
;(async function() {
// Await the result of Promise.any():
const result = await Promise.any([promise1, promise2, promise3])
console.log(result)
// Output:
// 'promise1 is resolved.', 'promise2 is resolved.' or 'promise3 is resolved.'
})()
// 例 2: 部分完成:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise1 is resolved.')
}, Math.floor(Math.random() * 1000))
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('promise2 was rejected.')
}, Math.floor(Math.random() * 1000))
})
;(async function() {
// Await the result of Promise.any():
const result = await Promise.any([promise1, promise2])
console.log(result)
// Output:
// 'promise1 is resolved.'
})()
// 例 3: 均被拒绝:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('promise1 was rejected.')
}, Math.floor(Math.random() * 1000))
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('promise2 was rejected.')
}, Math.floor(Math.random() * 1000))
})
;(async function() {
// Use try...catch to catch the AggregateError:
try {
// Await the result of Promise.any():
const result = await Promise.any([promise1, promise2])
}
catch (err) {
console.log(err.errors)
// Output:
// [ 'promise1 was rejected.', 'promise2 was rejected.' ]
}
})()
弱引用:WeakRef
最后一个抢眼的功能是 WeakRefs
。在 JavaScript 中,当你创建了一个创建对象的引用时,这个引用可以防止对象被 gc 回收,也就是说 JavaScript 无法删除对象并释放其内存。只要对该对象的引用一直存在,就可以使这个对象永远存在。
ES2021 了新的类 WeakRefs
。允许创建对象的弱引用。这样就能够在跟踪现有对象时不会阻止对其进行垃圾回收。对于缓存和对象映射非常有用。
必须用 new
关键字创建新的 WeakRef
,并把某些对象作为参数放入括号中。当你想读取引用(被引用的对象)时,可以通过在弱引用上调用 deref()
来实现。下面是一个非常简单的例子。
const myWeakRef = new WeakRef({
name: 'Cache',
size: 'unlimited'
})
console.log(myWeakRef.deref())
// Output:
// { name: 'Cache', size: 'unlimited' }
console.log(myWeakRef.deref().name)
// Output:
// 'Cache'
console.log(myWeakRef.deref().size)
// Output:
// 'unlimited'
Finalizers 和 FinalizationRegistry
与 WeakRef
紧密相连的还有另一个功能,名为 finalizers 或 FinalizationRegistry
。这个功能允许你注册一个回调函数,这个回调函数将会在对象被 gc 回收时调用。。
// 创建 FinalizationRegistry:
const reg = new FinalizationRegistry((val) => {
console.log(val)
})
;(() => {
// 创建新对象:
const obj = {}
//为 “obj” 对象注册 finalizer:
//第一个参数:要为其注册 finalizer 的对象。
//第二个参数:上面定义的回调函数的值。
reg.register(obj, 'obj has been garbage-collected.')
})()
// 当 "obj" 被gc回收时输出:
// 'obj has been garbage-collected.'
官方建议不要轻易使用 WeakRef
和 finalizer。其中一个原因是它们可能不可预测,另一个是它们并没有真正帮 gc 完成工作,实际上可能会gc的工作更加困难。你可以在它的提案(https://github.com/tc39/proposal-weakrefs#a-note-of-caution)中详细了解其原因。
总结
与以前的 JavaScript 规范(例如 ES6 和 ES2020)相比,看上去 ES2021的更新不多,但是这些有趣的功能值得我们关注。