一起养成写作习惯!这是我参与「掘金日新计划 · 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的几个新特性还是挺好用的,我们可以开始在项目中使用新特性。