15

简述

ES2021(ES12)将于 2021 年中发布。在本文中,你将将会了解五个最有趣的功能:String.prototype.replaceAll(),数字分隔符,逻辑赋值运算符,Promise.any()WeakRef 和Finalizers。

本文所描述的五个功能目前都处于第 4 阶段。这意味着它们已经完成,并将要在 JavaScript 引擎中实现了。这意味着你不会浪费时间去学习一些可能永远也不会出现的东西。

这些功能不久将会发布。如果有兴趣,可以到官方 Ecma TC39 GitHub 去了解有关其他提案的更多信息。这个 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 是假,则不会进行赋值。

如果 xy 都是假,也会发生同样的情况。最后是 x ?? = y。仅当 xnullundefined 时,才将 y 分配给 x。如果 x 既不是 null 也不是 undefined 则不会进行赋值,如果 ynullundefined 也一样。

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的工作更加困难。你可以在它的提案中详细了解其原因。

总结

与以前的 JavaScript 规范(例如 ES6 和 ES2020)相比,看上去 ES2021的更新不多。但是这些有趣的功能值得我们关注。

173382ede7319973.gif


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章


欢迎继续阅读本专栏其它高赞文章:



疯狂的技术宅
44.4k 声望39.2k 粉丝