5个值得你马上使用的ES12新特性

175 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

ECMAScript 2021(第 12 版)已由 ECMA International 于 2021 年 6 月 22 日最终确定,它带来了新特性和语法改进。。这些改进使得JavaScript 更加健壮并帮助开发人员轻松完成他们的工作。

本文将详细展示 ECMAScript 2021 提供的 5 大功能,以便您在项目中使用,并改善JavaScript的使用体验。

EcMAScript 2021提供的5个JavaScript新特性

  • 数字分隔符
  • String.prototype.replaceAll
  • Promise.any() 返回第一个fullfilled 的 promise ,若全部 reject,则返回一个带有失败原因的 AggregateError。
  • 逻辑赋值运算符
  • 私有类方法和访问器

1.数字分隔符

数字分隔符允许我们在数字中的数字之间添加下划线,这使它们更具可读性。这些下划线将在解析文件时自动删除。

// Decimal integer literal with digits grouped by thousand.
let n1 = 1_000_000_000;
console.log(n1); // This will print: 1000000000

// Decimal literal with digits grouped by thousand.
let n2 = 1_000_000_000.150_200
console.log(n2); // This will print: 1000000000.1502

// Hexadecimal integer literal with digits grouped by byte.
let n3 = 0x95_65_98_FA_A9
console.log(n3); // This will print: 641654651561

// BigInt literal with digits grouped by thousand.
let n4 = 155_326_458_156_248_168_514n
console.log(n4); // This will print: 155326458156248168514n

2.String.prototype.replaceAll

如果在字符串上使用replace(),则它只替换该值的第一个匹配项, 在过去,如果想要替换所有的匹配项,只能使用正则表达式。而现在字符串原型上的replaceAll()函数可以替换字符串的所有的匹配项

const orgStr = 'JavaScript, often abbreviated as JS, is a programming language that conforms to the ECMAScript specification. JavaScript is high-level, often just-in-time compiled and multi-paradigm.';

// 替换第一个, 使用 replace().
let newStr = orgStr.replace('JavaScript', 'TypeScript');
console.log(newStr);

// 替换所有的匹配, 使用 replaceAll().
let newStr2 = orgStr.replaceAll('JavaScript', 'TypeScript');
console.log(newStr2);

3.Promise.any()和AggregateError

Promise.any()和Promise.all()是相反的,任何一个promise得到fulfilled,将会触发any(),而Promise.all()将等待所有的promise得到fulfilled。以下是any()、all()、race()和allSettled()的区别。

  • Promise.all (ES2015) 只有当传入的每个 Promise 实例(p1,p2,p3)的状态都变成 fulfilled 时,p 才 fulfilled,只要(p1,p2,p3)有一个被 rejected,p 的状态就变成 rejected。
  • Promise.race (ES2015) 当传入的 Promise 实例(p1,p2,p3)中有一个率先改变状态,那么 p 的状态就跟着改变,也就是说返回最先改变的 Promise 实例的返回值。
  • Promise.allSettled (ES2020) 只有等到所有传入的 Promise 实例(p1,p2,p3)都返回结果,不管是 fulfilled 还是 rejected,包装实例才会结束。
  • Promise.any (ES2021) 当其中任何一个 Promise 完成(fulfilled)时,就返回那个已经有完成值的 Promise。如果所有的 Promise 都拒绝 (rejected), 那么返回一个拒绝的 Promise。

参考以下代码,了解如何使用promise.any()

// Create a Promise.
const promise1 = new Promise((resolve, reject) => {
    // After 2 seconds resolve the first promise.
    setTimeout(() => resolve("The first promise has been resolved."), 2000);
});

// Create a Promise.
const promise2 = new Promise((resolve, reject) => {
    // After 1 second resolve the second promise.
    setTimeout(() => resolve("The second promise has been resolved."), 1000);
});

// Create a Promise.
const promise3 = new Promise((resolve, reject) => {
    // After 3 seconds resolve the third promise.
    setTimeout(() => resolve("The third promise has been resolved."), 3000);
});

(async function () {
    const data = await Promise.any([promise1, promise2, promise3]);
    // Print the data returned from the first resolved Promise.
    console.log(data);
    // The above will print: The second promise has been resolved.
})();

如果所有承诺都被rejected,那么将抛出AggregateError异常。请参阅以下代码,了解如何处理该异常。

// Create a Promise.
const promise1 = new Promise((resolve, reject) => {
    // After 1 second reject the first promise.
    setTimeout(() => reject("The first promise has been rejected."), 1000);
});

// Create a Promise.
const promise2 = new Promise((resolve, reject) => {
    // After 500 miliseconds reject the second promise.
    setTimeout(() => reject("The second promise has been rejected."), 500);
});

// Try executing the Promises.
(async function () {
    try {
        const data = await Promise.any([promise1, promise2]);
        console.log(data);
    } catch (error) {
        // If all Promises gets rejected, then this try-catch block will handle
        // the aggregate errors.
        console.log("Error: ", error);
    }
})();

4.逻辑赋值运算符

在ECMAScript 2021更新中引入了三个逻辑赋值运算符。它们是逻辑运算符和赋值表达式的组合。

  • 逻辑或赋值运算符 ||=
  • 逻辑与赋值运算符 &&=
  • 空合并赋值运算符 ??=

4.1. 逻辑或赋值运算符

逻辑或赋值运算符||=接受两个操作数,如果左操作数为false,则将右操作数赋值给左操作数。请参阅以下代码,了解如何使用逻辑或赋值运算符。

// In the example, the ||= will check if the songsCount is false (0).
// If false, then the right value will be assigned to the left variable.
let myPlaylist = {songsCount: 0, songs:[]};
myPlaylist.songsCount ||= 100;
console.log(myPlaylist); // This will print: {songsCount: 100, songs: Array(0)}

逻辑或赋值运算符短路。此运算符||=相当于以下语句。

a || (a = b)

4.2. 逻辑与赋值运算符

逻辑与赋值运算符&&=仅当左操作数为真时,才将右操作数赋值给左操作数。请参阅以下代码,了解如何使用逻辑与赋值运算符。

// In the example, the &&= will check if the filesCount is true.
// If true, then the right value will be assigned to the left variable.
let myFiles = {filesCount: 100, files:[]};
myFiles.filesCount &&= 5;
console.log(myFiles); // This will print: {filesCount: 5, files: Array(0)}

逻辑与赋值运算符也会短路。此运算符&&=相当于下面使用逻辑AND运算符的语句。

a && (a = b)

4.3. 空赋值运算符

空合并赋值运算符??=仅当左操作数为空或未定义时,才将右操作数赋给左操作数。请参阅以下代码,了解如何使用空合并赋值运算符。

// In the example, the ??= will check if the lastname is null or undefined.
// If null or undefined, then the right value will be assigned to the left variable.
let userDetails = {firstname: 'Katina', age: 24}
userDetails.lastname ??= 'Dawson';
console.log(userDetails); // This will print: {firstname: 'Katina', age: 24, lastname: 'Dawson'}

空合并赋值运算符也会短路。这个运算符??=相当于下面使用空合并运算符的语句。

a ?? (a = b)

5.私有类方法和访问器

默认情况下,类方法和属性是公共的,但私有方法和属性可以使用#前缀创建。私有封装已经从ECMAScript 2021更新中可以使用。这些私有方法和属性只能从类内部访问。请参阅以下代码,了解如何使用私有方法。

// Let's create a class named User.
class User {
    constructor() {}

    // The private methods can be created by prepending '#' before
    // the method name.
    #generateAPIKey() {
        return "d8cf946093107898cb64963ab34be6b7e22662179a8ea48ca5603f8216748767";
    }

    getAPIKey() {
        // The private methods can be accessed by using '#' before
        // the method name.
        return this.#generateAPIKey();
    }
}

const user = new User();
const userAPIKey = user.getAPIKey();
console.log(userAPIKey); // This will print: d8cf946093107898cb64963ab34be6b7e22662179a8ea48ca5603f8216748767

私有访问器是私有的Getters 和 Setters。Getter获取类属性的值,Setter为类属性赋值。可以使用#前缀定义私有getter。

get #newAccountPassword() {}

类似地,可以通过使用#前缀定义私有setter。

set #generateAccountPassword(newPassword) {}

参考下面的代码,了解如何使用私有getter和setter。

// Let's create a class named Str.
class Str {
    // The private attributes can be created by prepending '#'
    // before the attribute name.
    #uniqueStr;

    constructor() {}

    // A private Setters can be created by prepending '#' before
    // the Setter name.
    set #generateUniqueStringByCustomLength(length = 24) {
        const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        let randomStr = "";

        for (let i = 0; i < length; i++) {
            const randomNum = Math.floor(Math.random() * characters.length);
            randomStr += characters[randomNum];
        }

        this.#uniqueStr = randomStr;
    }

    // Public Setter
    set setRandomString(length) {
        this.#generateUniqueStringByCustomLength = length;
    }

    // A private getter can be created by prepending '#' before
    // the Getter name.
    get #fetchUniqueString() {
        return this.#uniqueStr;
    }

    // Public Getter
    get getRandomString() {
        return this.#fetchUniqueString;
    }
}

const str = new Str();
// Calling a public Setter which will then access the private Setter
// within the class.
str.setRandomString = 20;

// Calling a public Getter which will then access the private Getter
// withing the class.
const uniqueStr = str.getRandomString;
console.log(uniqueStr); // This will print a random string everytime you execute the Getter after the Setter.

总结

ES12的几个新特性还是挺好用的,我们可以开始在项目中使用新特性。

译自:levelup.gitconnected.com/top-5-javas…