这难道不是你期待的ES2021新特性吗?

2,121 阅读7分钟

ES2020发布还没多久,但是更多的功能点已经安排上了,等待发布。TC39工作组负责处理这些新的功能点到新的ECMAScript标准版本。本篇文章将介绍已经在TC39 Stage4的功能点。TC39提案有以下4个阶段:

Stage 0: Strawperson:

由TC39成员提议任何想法、修改、以前没有被提议的(或以前的提议被否决的),然后将其添加到 Stage 0 提案的页面中。

Stage 1: Proposal:

提案正式由TC39成员创立,提案描述一个结构化问题和解决方案。

Stage 2: Draft:

使用正式的规范语言精确描述提案的语法和语义。

Stage 3: Candidate:

提案的规范和用户的反馈已经完成。

Stage 4:FInished:

提案将在接下来的ECMAScript版本出现。

ES2021将在2021年6月发布。

逻辑赋值运算符

逻辑赋值运算符结合逻辑运算符和赋值运算符,它让代码变得简短、让变量和对象属性条件赋值变得简单。ES2021将推出三个新的逻辑操作符,每一个操作符都有两个运算元。

||=  // combines logical OR with ASSIGN
&&=  // combines logical AND with ASSIGN
??=  // combines NULLISH with ASSIGN

逻辑OR ||=操作符是结合了逻辑||操作符和赋值操作符。当前面的值是假值的时候将右边运算元赋值给左边运算元,相当于形式a || (a = b)

let thirdPlace = 5;
let secondPlace = false;
let firstPlace = null;

thirdPlace  ||= 3;
secondPlace ||= 2;
firstPlace  ||= 1;

console.log(thirdPlace);    // "5"
console.log(secondPlace);   // "2"
console.log(firstPlace);    // "1"

同样的,逻辑AND赋值运算符&&=结合逻辑 &&操作符和赋值操作符,当且仅当运算符左侧的值是真值的时候将运算元右侧的值赋值给运算元左侧,相当于a && (a = b)的简写形式。

let thirdPlace = 5;
let secondPlace = false;
let firstPlace = null;

thirdPlace  &&= 3;
secondPlace &&= 2;
firstPlace  &&= 1;

console.log(thirdPlace);    // "5"
console.log(secondPlace);   // "false"
console.log(firstPlace);    // "null"

空值赋值操作符??=a??(a=b)的简写形式。当且仅当前面一个值是null或者undefined的时候将第二个运算元赋值给第一个运算元。

let thirdPlace = 5;
let secondPlace = false;
let firstPlace = null;

thirdPlace  ??= 3;
secondPlace ??= 2;
firstPlace  ??= 1;

console.log(thirdPlace);    // "5"
console.log(secondPlace);   // "false"
console.log(firstPlace);    // "1"

当初始化对象属性的时候,这些新操作符非常好用。这些操作符帮助我们保证在person对象内部的firstName属性在没有赋值的时候已经初始化了Commander值。

以前的精简形式:

(() => {
    const person = {
        firstName: 'Unnamed'
    }
    person.firstName || (person.firstName = 'Commander');
    console.log(person.firstName); // "Unnamed"
    person.lastName || (person.lastName = 'Shepard');
    console.log(person.lastName); // "Shepard"
})();

ES2021整个结构更加精简:

(() => {
    const person = {
        firstName: 'Unnamed'
    }
    person.firstName ||= 'Commander';
    console.log(person.firstName); // "Unnamed"
    person.lastName ||= 'Shepard';
    console.log(person.lastName); // "Shepard"

})();

总结来说,你只是简化了赋值默认值而不是初始化变量。 从:

person.firstName = person.firstName|| person.firstName='Commander';

到:

person.firstName ||= 'Commander';

注意: 除此之外,采用老的方式实现这种形式,将阻断可能存在的getter方法。另一种方式是如果存在__getter的话就调用__getter

字符串替换

当替换一个字符串中所有存在的子序列时,最好的方式是使用正则表达式。你也可以使用polyfill的函数replaceAllPolyfill(),这个方法对字符串产生一个各自的具体的正则表达式全局搜索。

const winnersPhrase = 'This 111 is the 111 text 111 to gratulate 111 the winners 111 of the 111 race!';

const replaceAllPolyfill = (string, find, replace) => {
    return string.replace(new RegExp(find, 'g'),
        replace);
}

const correctPhrase = replaceAllPolyfill(winnersPhrase, ' 111', '');
console.log(correctPhrase); // This is the text to gratulate the winners of the race!

另外一种实现形式是采用字符串方法split(),这个方法以参入的子序列为节点分割字符串为数组,然后再针对这个数组调用call()方法,声明新的插入字符串,将所有的子序列合在一起。

const winnersPhrase = 'This 111 is the 111 text 111 to gratulate 111 the winners 111 of the 111 race!';

const replaceAllPolyfill = (string, find, replace) => {
    return string.replace(new RegExp(find, 'g'),
        replace);
}

const correctPhrase = winnersPhrase.split(' 111'). join('');
console.log(correctPhrase); // This is the text to gratulate the winners of the race!

出于性能原因这些方式都是低效的。对于长字符串,频繁的出现字符串搜索。replaceAll()方法在未来会简化这个流程,它将替换字符串中所有存在的与第一个参数相同的子序列为第二个参数的字符串序列。

const winnersPhrase = 'This 111 is the 111 text 111 to gratulate 111 the winners 111 of the 111 race!';

const correctPhrase = winnersPhrase.replaceAll(' 111', '');
console.log(correctPhrase); // This is the text to gratulate the winners of the race!

你可以看出来,我们可以优化一切以减少代码编写和更加高效。

Promises

ES2020已经通过了Promise的allSettled()方法。ES2021 Promise阵营将有一个新的成员,any()。它期望一个Iterable的prmoses,然后返回一个最快fulfilled状态的promise。当所有的promise都rejected了,any将返回一个rejected的promise。

(async () => {
    const promisesToBeResolved = [
        Promise.resolve(555),
        Promise.resolve(666),
        Promise.resolve(777),
    ];
    try {
        const firstResolver = await Promise.any(promisesToBeResolved);
        console.log(firstResolver); // --> 555
    } catch (error) {
        console.error(error);
    }
})();

(async () => {
    const promisesSomeToBeRejected = [
        Promise.reject(555),
        Promise.reject(666),
        Promise.resolve(777),
    ];
    try {
        const firstResolver = await Promise.any(promisesSomeToBeRejected);
        console.log(firstResolver);  // --> 777
    } catch (error) {
        console.error(error);
    }
})();

(async () => {
        const promisesAllToBeRejected = [
            Promise.reject(1),
            Promise.reject(2),
            Promise.reject(5),
        ];
        try {
            const firstResolver = await Promise.any(promisesAllToBeRejected);
            console.log(firstResolver);
        } catch (error) {
           console.error(error);  // All promises were rejected
        }
    }
)();

可以看出这个例子,any()方法等待第一个resolved值,而且只会打印firstResolver,当所有的promise都rejected,它将在trycatch中抛出错误,第三个例子清楚的证明了为什么程序没有打印出firstResolver

WeakRefs

我敢打赌你知道WeakSetWeakMap,他们从ES2015开始被实现的。WeakSet是对象的集合,WeakMap是键值对的集合,而他的键必须是一个对象。他们之间的联系属于弱引用。也就是说这些对象作为垃圾回收的一部分,如果没有其他强引用,可以从内存中被移除。一个对象的弱引用是不会阻止垃圾回收器移除内存中的这个对象。相反,一个普通(强)引用的对象会保留这个对象在内存中。

ES2021将可以实现人为使用WeakRef对象而不用WeakSetWeakMap来创建一个对象的若引用。这个符合需要的提案警告你在使用WeakRef时一定要小心。总之,使用之前一定有合理理由。根本上来说,我们应该尽可能的避免使用WeakRef。垃圾回收器已经够复杂和系统依赖的,所以这很难去预测垃圾回收器的具体行为。

二进制、十六进制和大数终可读

以前容易读懂/分辨大数吗?我看未必。同样的情况出现在长整形,二进制和十六进制数字。所以为什么要等到2021年才来介绍可读性强的大数字呢?我也不知道,但是其他语言(Java、Python、Perl、Ruby、C#)都表现的良好,闲杂也该JavaScript了吧。这个数字分隔符将唤醒我们的眼睛。

const goldstack1 = 785_00;
const gemstack = 1_000_000_000;
console.log(goldstack1);                // 78500
console.log(gemstack);                  // 1000000000
console.log(goldstack1 === 78500);      // true
console.log(gemstack === 1000000000);   // true

我们通过在数字相符的组之间放置分隔符使数字值更加的可读,分隔符适用于十进制表示法的数字以及二进制或十六进制表示法的数字,以及ES2020中引入的BigInts。

const goldStack = 0b1111_1111_1110_0101;
console.log(goldStack);                        // 65509
console.log(goldStack === 0b1111111111100101); // true
const goldStack = 0xFF_AC_C1;
console.log(goldStack);                 // 16755905
console.log(goldStack === 0xFF_AC_C1);  // true
const goldtstack = 1_000_000_000_000_000n;
console.log(goldtstack);                            // 1000000000000000n
console.log(goldtstack === BigInt(10 ** 15)); // true

注意: 分隔符不会影响===操作符比较两个值

总结

JavaScript标准在逐步稳定,尽管这个发布将在几个月之后,一些非常靓丽的功能已经完成,等待2021版本的带来。如果所有这些功能都添加到新的版本中,这将是JavaScript另一个大的进步。

TC39: github.com/tc39/propos…

TC39 Stage 4:github.com/tc39/propos…. [1], [2]

原文: medium.com/front-end-w…