本文转载自公众号“读芯术”(ID:AI_Discovery)。
2020年可能已经位列史上最糟糕年份TOP5了。那么不妨忘记今年,期待一下2021会有什么惊喜吧!没错,明年我们将迎来新版ECMAScript(也就是常说的JavaScript)。本文中,笔者将快速带你先来了解一下其为何物。
replaceAll字符串的方法
这一方法早就有了,现有的 replace方法早就应该有所成效。也许你不知道,目前来自String对象的 replace方法只会影响所找到的第一个匹配,当然,除非你使用正则表达式而不是简单的字符串作为第一个参数。
这并非是很大的进步,更多的是一种补充,但还是值得赞赏。
- const str ="The brown fox is reallybrown";
- //Instead of doing this... const newStr = str.replace(/brown/g, "White");
- //You'll be able to do this...
- const newStr = str.replaceAll("brown", "White");
方法和访问器的私密化修改器
图源:unsplash
目前还没听说私密化有何进展(该提案还停留在第三阶段),但是作为未来的一个尝试,我们将能够对方法和访问器设置可见性。这里的“可见性”是指将它们设置为私人化,意味着我们终于有了保护代码的基本方式,并开始慢慢地走向更面向OOP的编码方式(并不是说我们现在的函数式方法有什么问题)。
- classPerson {
- constructor(name, birhdate, city) {
- this.name= name;
- this.birthdate= birthdate;
- this.city= age;
- } get #Age() {
- //return actualage
- }
- #myPrivateMethod() {
- console.log("You can't use this from outside of this class")
- }
- }
实质上,你正在用#字符来使方法或访问器私密化。这个字符也会成为该名称的一部分,所以如果你想使用上面所提到的方法,可以简单地用 this.#Age 或者 this.#myPrivateMethod()。
在这两种情况下,如果你试图从一类之外甚至从扩展了的类里面使用它们,都会出现异常(要等到有了保护方法之后再使用)。
终结器和弱引用
这两个有趣的工具可以帮助处理内存使用问题,并管理垃圾收集。不过,除非你需要特别注意内存的使用,不然不太需要用到这两个工具。
注意,下面的两个功能涉及垃圾收集器的工作方式,但安装启用只针对一个运行时。这意味着编写依赖于非标准安装的业务逻辑很可能会产生意想不到的结果。即使有了这些工具,也要在使用前要清楚自己的目的。
(1) 弱引用
在介绍弱引用之前,我先快速介绍一下强引用,以便你理解弱引用的优势。强引用本质上是一个指向目标的指针,在JavaScript中,这只是一个变量,你已经将目标分配到这个变量中。比如说:
- classPerson {
- constructor(first_name, last_name) {
- this.first_name = first_name;
- this.last_name = last_name;
- }}let myP = new Person("Fernando", "Doglio")
上文中,myP是一个有效的强引用,直到它不复存在。一旦对一个对象的所有强引用被消除,那么垃圾收集器就可以自由地销毁这个对象,并释放它的内存以便用于其他事情。也就是说,有些情况下,比如下面这种,强引用可能会锁定一个目标,以至于永远无法释放。
- classPerson {
- constructor(first_name, last_name) {
- this.first_name = first_name;
- this.last_name = last_name;
- this.sibling = null; }
- }last me = new Person("Fernando", "Doglio");
- last sibling = new Person("My", "Sibling");
- me.sibling = sibling;sibling.sibling = me;
在上面的例子中,两个对象都是相互引用的,所以即使me和sibling变量都不在范围内,因此,去掉引用,在内部每个对象都对另一个对象有一个强引用。在实践中,这意味着这些对象永远不会被收集。这么做完全没有问题,除非你是在一个内存非常小的设备中使用JS。
(2) 输入弱参考
对强引用有所了解之后,弱引用就很好理解啦。弱引用本质上是一种保留对象引用而不影响垃圾收集器行为的方法。在前面的例子中,如果sibling性能会使用弱引用构造来设置,那么就可以收集这些对象。弱引用的另一个用例是缓存结构,因为人们不希望缓存的对象内部引用过于活跃。
- classMyCache {
- constructor() {
- this.cache= {}
- } add(key, obj) { this.cache[key] =newWeakRef(obj)
- } get(key) {
- let cachedRef = this.cache[key].deref()
- if(cachedRef) return cachedRef;
- returnfalse; } }
上面的例子非常基础,使用弱引用就是这么简单。只要记住,如果你想访问被引用的对象,要用deref方法。因为这是一个弱引用,就需要检查deref的返回值,如果它是未定义的,就说明这个对象不再存在,否则可以安全地使用它(因此在 get 方法中进行IF检查)。
(3) 终结器
现在,在顶部的 cherrie 中,终结器允许你对弱引用对象收集垃圾的事实做出反应。同样,这也是高度具体化的实现,以下是使用方法:
- let registry =newFinalizationRegistry( value => {
- console.log("An object was removed! Message:", value)
- }) let myObject = {
- //....
- }
- registry.register(myObject, "myObject was destroyed")
本质上,这里用了FinalizationRegistry 来创建注册表,需要用回调函数作为参数。在每次收集一个对象(之前用register 方法注册的)时都会用到这个函数。
register 方法本身是用来指定所等待的对象的重构方法,它的第二个参数是在创建注册表时传递给初始定义的回调的值。建议你少用这个方法,特别是不要想着用它来做业务,但是可以考虑在特殊情况下用它来解决一些新奇的问题。
Promise.any
图源:unsplash
在这个版本中,ECMAScript中增加了一个同时处理多个承诺的方法。any方法可以运行多个承诺,并用第一个解决的承诺来解决后面的问题,或者等到所有的承诺都失败并返回到一个AggregateError对象处(它是Error对象的一个子类)。
那么,any和race之间有什么区别呢?事实上,race只要有一个承诺会实现或失败,它就会进行结算并返值。然而,any 会用第一个结算值进行结算,或者等待所有的值都失败了,再一起恢复所有错误。
- const promise1 =Promise.reject(0);
- const promise2 =newPromise((resolve) =>setTimeout(resolve, 100, 'quick'));
- const promise3 =newPromise((resolve) =>setTimeout(resolve, 500, 'slow'));
- const promises = [promise1, promise2, promise3]; Promise.race(promises).then((value) => console.log(value)); //logs the rejected promise
- //vs
- Promise.any(promises).then((value) => console.log(value)); //logs "quick"
注意any实际上忽略了第一个被拒绝的承诺,因为还有其他的承诺正在解决,这就是race和any的主要区别。
ECMAScript2021的新特征不多,但已经被接受的功能笔者看来非常好!使用WeakRef和FinalizationRegistry时要小心哦,它们的功能非常有趣,但它们在不同的运行情况下处理的结果或行为可能会不一样。
你最喜欢哪个新功能?对于这个版本,你最期待的又是什么呢?