前言
TC39 委員會于近期批準(zhǔn)了 ECMAScript 2020(即 ES2020)候選提案,即經(jīng)審定最終添加到 JavaScript 語言中的特性集。ES2020 候選提案是今年六月提交 ECMA 大會(General Assembly)的審批匯總儒将。
1. Promise.allSettled
Promise.all 缺陷
都知道 Promise.all 具有并發(fā)執(zhí)行異步任務(wù)的能力竹椒。
先復(fù)習(xí)一下Promise.all 的用法
let p1 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)1"]
})
let p2 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)2"]
})
let p3 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)3"]
})
?
Promise.all([p1,p2,p3]).then((result) => {
// all方法并發(fā)的三個promise都是成功狀態(tài)會執(zhí)行
console.log(result);
/*
返回的結(jié)果: [{…}, {…}, {…}]
*/
}).catch((error) => {
console.log(error);
})
?
但時Promise.all 方法最大問題就是如果其中某個任務(wù)出現(xiàn)異常(reject)辜羊,所有任務(wù)都會掛掉,Promise直接進(jìn)入 reject 狀態(tài)。
let p1 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)2"]
})
let p2 = Promise.reject({
code: 500,
errorMsg:"服務(wù)器錯誤"
})
?
let p3 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)3"]
})
?
Promise.all([p1,p2,p3]).then((result) => {
// 如果有一個promise 是reject,則不會執(zhí)行這個回調(diào)函數(shù)
console.log(result);
}).catch((error) => {
console.log(error);
// 因?yàn)閜2 是reject 所以會執(zhí)行這個回到函數(shù)
// {code: 500,errorMsg:"服務(wù)器錯誤"}
})
如果小伙伴們在測試代碼的時候,記得重新打開頁面,不要刷新.
想象這個場景:在你的項(xiàng)目中,使用 Promise.all 來并發(fā)三個接口,每個接口都單獨(dú)對應(yīng)一個接口,請求數(shù)據(jù), 如果其中任意一個接口服務(wù)異常鳞尔,狀態(tài)是reject,這會導(dǎo)致三個區(qū)域數(shù)據(jù)全都無法渲染出來,因?yàn)槿魏?reject 都會進(jìn)入catch回調(diào), 很明顯早直,這是無法接受的.
Promise 在 ES2020 中新增了 Promise.allSettled() 寥假,就能好的 幫我們解決這個問題,Promise.allSetted 方法,無論一個任務(wù)是正常或是異常,都會返回對應(yīng)的狀態(tài)(fulfilled或者rejected)與結(jié)果(value),
let p1 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)2"]
})
let p2 = Promise.reject({
code: 500,
errorMsg:"服務(wù)器錯誤"
})
?
let p3 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)3"]
})
?
Promise.allSettled([p1,p2,p3]).then((result) => {
// Promise.allSettled會在這里中處理并發(fā)promise狀態(tài)和結(jié)果
console.log(result);
/**
返回的結(jié)果:
[
{status: "fulfilled", value: {…}}
{status: "rejected", reason: {…}}
{status: "fulfilled", value: {…}}
]
*/
?
})
這樣我們在then方法中得到所有結(jié)果以后,我們就可以使用fill來過濾狀態(tài)為rejected掉的數(shù)據(jù)
2. String.prototype.matchAll
String.prototype上的match()方法僅返回完全匹配霞扬,但是沒有返回關(guān)于特定正則組的任意信息糕韧。String.prototype.matchAll可以返回比match()多很多的信息拾给。返回的迭代器除了精確匹配外還給了我們訪問所有的正則匹配捕獲組。
還節(jié)的match方法嗎?看下面一段代碼
let str = "<div>this is div</div><p>this is JS</p>";
let reg = /<\w+>(.*?)<\/\w+>/g;
console.log(str.match(reg));
/**
返回結(jié)果:
["<div>this is div</div>", "<p>this is JS</p>"]
*/
我們都知道兔沃,match是可以正常匹配到所有匹配項(xiàng)蒋得,但卻沒辦法匹配到子項(xiàng)(group)。如果想要匹配子項(xiàng)乒疏,那么需要把全局匹配 /g 標(biāo)識去掉额衙。
let str = "<div>this is div</div><p>this is JS</p>";
let reg = /<\w+>(.*?)<\/\w+>/;
console.log(str.match(reg));
/**
[
0: "<div>this is div</div>"
1: "this is div"
groups: undefined
index: 0
input: "<div>this is div</div><p>this is JS</p>"
length: 2
]
**/
這樣可以獲取到匹配的父項(xiàng),包括子項(xiàng)(group)怕吴,但只能獲取到第一個滿足的匹配字符窍侧。
如果既想要匹配所有匹配項(xiàng),又想要匹配子項(xiàng)转绷,那么 match() 是無法滿足的伟件。ES2020 提供了 matchAll() 方法.
注意matchAll方法返回的是迭代器,因此我們需要遍歷獲取結(jié)果
let str = '<div>this is div</div><p>this is JS</p>';
let reg = /<\w+>(.*?)<\/\w+>/g;
let allMatchs = str.matchAll(reg);
?
for(let match of allMatchs){
console.log(match)
}
/**
第一次遍歷的結(jié)果:
[
0: "<div>this is div</div>"
1: "this is div"
groups: undefined
index: 0
input: "<div>this is div</div><p>this is JS</p>"
length: 2
]
第二次遍歷的結(jié)果:
[
0: "<p>this is JS</p>"
1: "this is JS"
groups: undefined
index: 22
input: "<div>this is div</div><p>this is JS</p>"
length: 2
]
**/
3. import()
目前前端項(xiàng)目打包的資源越來越大,但應(yīng)用初始化時資源并不需要全量加載议经,為了提高頁面性能斧账,往往需要按需加載資源。Domenic Denicola提案的動態(tài)導(dǎo)入可以實(shí)現(xiàn)按需加載煞肾。這個類似函數(shù)的格式(不是繼承自Function .prototype)返回一個很強(qiáng)大的promise咧织。使用場景比如: 按需導(dǎo)入,在一個腳本中計(jì)算模塊名并加載執(zhí)行變得可能籍救。
element.onclick = () =>{
import("/js/helpers.js")
.then((module) =>{
console.log(module)
})
.chatch((err) => {
// load err
console.log(err)
})
}
因此我們也可以使用async異步函數(shù)來配置處理
element.onclick = async () =>{
let module = await import("/js/helpers.js")
// 處理 導(dǎo)入module模塊
}
4. BigInt
JavaScript 中 Number 類型都保存為 64 位浮點(diǎn)數(shù)习绢,精確度只能到 53 位,也就是說Js 中 Number類型只能安全的表示-(2^53-1)至 2^53-1 范的值,超出這個范圍的整數(shù)計(jì)算或者表示會丟失精度蝙昙。
console.log(Math.pow(2, 53)); // 9007199254740992
console.log(Math.pow(2, 53) + 1); // 9007199254740992
console.log(Math.pow(2, 53) === Math.pow(2, 53) + 1); // true
且無法正確表示大于或等于 2^1024 的數(shù)值闪萄。
console.log(Math.pow(2, 1023)); // 8.98846567431158e+307
console.log(Math.pow(2, 1024)); // Infinity
ES2020 中引入了新的數(shù)據(jù)類型 BigInt, 讓Number.MAXSAFEINTEGER不再是JavaScript中的一個限制。BigInt是一個能表示任意精度整數(shù)的新基礎(chǔ)類型奇颠。你可以通過使用BigInt方法或者在一個數(shù)字后添加n后綴來把一個數(shù)字轉(zhuǎn)換為一個新的bigint類型败去。
// 使用BigInt
// 1. 字面量方式在數(shù)字字面量后面加n
let num = 123n;
console.log(num); // 123n
console.log(typeof num); // bigint
?
// 2. 函數(shù)執(zhí)行的方式
let number = BigInt(Math.pow(2, 53) + 1);
console.log(number); // 9007199254740992n
console.log(typeof number); // bigint
那么我們看看BigInt處理大型數(shù)字相加處理
let number = BigInt(Math.pow(2, 53));
letnumber2 = BigInt(Math.pow(2, 53));
console.log(number); // 9007199254740992n
console.log(number2); // 9007199254740992n
?
letnum = number + number2;
console.log(num); // 18014398509481984n
console.log(num.toString()); // 18014398509481984
注意:
BigInt 是一種新的數(shù)據(jù)原始(primitive)類型。
5. for-in機(jī)制
ECMAScript遺留了一個關(guān)于for-in循環(huán)順序的詳細(xì)描述大刊。在 ECMA-262 5rd Edition 中對遍歷機(jī)制又進(jìn)行了調(diào)整为迈,并未并且規(guī)定具體的規(guī)則,不同瀏覽器有不同的實(shí)現(xiàn)缺菌,這導(dǎo)致對屬性的遍歷順序存在不一致的問題。ES2020 中要求對象的遍歷實(shí)現(xiàn)上搜锰,各瀏覽器要保持一致伴郁。
6. 可選鏈(Optional chaining)
以前在處理多層對象屬性值獲取的時候,通常需要對各層級的屬性進(jìn)行校驗(yàn).
例如
let name = user && user.info && user.info.name
這是一種丑陋但又不得不做的前置校驗(yàn),否則很容易命中 Uncaught TypeError: Cannot read property… 這種錯誤蛋叼,這極有可能讓你整個應(yīng)用掛掉焊傅。
對此剂陡,ES2020 進(jìn)行了優(yōu)化,可以通過 ?. 來簡化校驗(yàn)狐胎。
?. 操作符與 . 類似鸭栖,兩者的區(qū)別在于,?. 在獲取對象屬性時握巢,如果其引用對象為 null 或 undefined晕鹊,則表達(dá)式會發(fā)生短路暴浦,直接返回 undefined溅话。
示例代碼如下:
// 1. 能正確找到屬性值
let user = {
info:{
name:"小明"
}
}
var name = user?.info?.name;
console.log(name); // 小明
?
// 2. 屬性值不存在
let user2 = {
info:{
city:"上海"
}
}
var name2 = user2?.info?.name;
console.log(name2); // undefined
7. 空值合并運(yùn)算符(Nullish coalescing Operator)
在JavaScript中我們經(jīng)常會遇到給某個變量或者對象的屬性添加默認(rèn)值
示例:
// 三目運(yùn)算符處理默認(rèn)值
const name = user.name ? user.name : "默認(rèn)名稱";
?
// 邏輯運(yùn)算符中的短路算法處理默認(rèn)值
const name = user.name || "默認(rèn)名稱"
ES2020 新增了更簡潔的空值合并操作符(??),左側(cè)值為 null 或 undefined 時返回右側(cè)默認(rèn)值
const name = user.name ?? "默認(rèn)名稱"
但對于邏輯或操作符來說歌焦,''飞几、0 都會轉(zhuǎn)化為 false,容易產(chǎn)生邏輯錯誤独撇。在業(yè)務(wù)上屑墨,大多是是想判斷變量是否為 undefined 或者 null。
因此要注意,
// user.name的值會有可能會進(jìn)行隱式類型轉(zhuǎn)行,為0的時候,也會只用默認(rèn)值
const name = user.name || "默認(rèn)名稱" ;
?
// ES2020新增的?? 運(yùn)算符 有且僅當(dāng)user.name的為null或者undefined時
// 才會啟用默認(rèn)值
const name = user.name ?? "默認(rèn)名稱"