前言
TC39 委員會(huì)于近期批準(zhǔn)了 ECMAScript 2020(即 ES2020)候選提案轻黑,即經(jīng)審定最終添加到 JavaScript 語(yǔ)言中的特性集炮温。ES2020 候選提案是今年六月提交 ECMA 大會(huì)(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ā)的三個(gè)promise都是成功狀態(tài)會(huì)執(zhí)行
console.log(result);
/*
返回的結(jié)果: [{…}, {…}, {…}]
*/
}).catch((error) => {
console.log(error);
})
但時(shí)Promise.all 方法最大問(wèn)題就是如果其中某個(gè)任務(wù)出現(xiàn)異常(reject),所有任務(wù)都會(huì)掛掉,Promise直接進(jìn)入 reject 狀態(tài)糜烹。
let p1 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)2"]
})
let p2 = Promise.reject({
code: 500,
errorMsg:"服務(wù)器錯(cuò)誤"
})
let p3 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)3"]
})
Promise.all([p1,p2,p3]).then((result) => {
// 如果有一個(gè)promise 是reject,則不會(huì)執(zhí)行這個(gè)回調(diào)函數(shù)
console.log(result);
}).catch((error) => {
console.log(error);
// 因?yàn)閜2 是reject 所以會(huì)執(zhí)行這個(gè)回到函數(shù)
// {code: 500,errorMsg:"服務(wù)器錯(cuò)誤"}
})
如果小伙伴們?cè)跍y(cè)試代碼的時(shí)候,記得重新打開(kāi)頁(yè)面,不要刷新.
想象這個(gè)場(chǎng)景:在你的項(xiàng)目中,使用 Promise.all 來(lái)并發(fā)三個(gè)接口,每個(gè)接口都單獨(dú)對(duì)應(yīng)一個(gè)接口,請(qǐng)求數(shù)據(jù), 如果其中任意一個(gè)接口服務(wù)異常漱凝,狀態(tài)是reject,這會(huì)導(dǎo)致三個(gè)區(qū)域數(shù)據(jù)全都無(wú)法渲染出來(lái)疮蹦,因?yàn)槿魏?reject 都會(huì)進(jìn)入catch回調(diào), 很明顯,這是無(wú)法接受的.
Promise 在 ES2020 中新增了 Promise.allSettled() 茸炒,就能好的 幫我們解決這個(gè)問(wèn)題,Promise.allSetted 方法,無(wú)論一個(gè)任務(wù)是正炽岛酰或是異常,都會(huì)返回對(duì)應(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ù)器錯(cuò)誤"
})
let p3 = Promise.resolve({
code: 200,
list: ["數(shù)據(jù)3"]
})
Promise.allSettled([p1,p2,p3]).then((result) => {
// Promise.allSettled會(huì)在這里中處理并發(fā)promise狀態(tài)和結(jié)果
console.log(result);
/**
返回的結(jié)果:
[
{status: "fulfilled", value: {…}}
{status: "rejected", reason: {…}}
{status: "fulfilled", value: {…}}
]
*/
})
這樣我們?cè)趖hen方法中得到所有結(jié)果以后,我們就可以使用fill來(lái)過(guò)濾狀態(tài)為rejected掉的數(shù)據(jù)
2. String.prototype.matchAll
String.prototype上的match()方法僅返回完全匹配,但是沒(méi)有返回關(guān)于特定正則組的任意信息壁公。String.prototype.matchAll可以返回比match()多很多的信息感论。返回的迭代器除了精確匹配外還給了我們?cè)L問(wèn)所有的正則匹配捕獲組。
還節(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)比肄,但卻沒(méi)辦法匹配到子項(xiàng)(group)。如果想要匹配子項(xiàng)囊陡,那么需要把全局匹配 /g 標(biāo)識(shí)去掉芳绩。
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)撞反,但只能獲取到第一個(gè)滿足的匹配字符妥色。
如果既想要匹配所有匹配項(xiàng),又想要匹配子項(xiàng)遏片,那么 match() 是無(wú)法滿足的嘹害。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)目打包的資源越來(lái)越大鳍侣,但應(yīng)用初始化時(shí)資源并不需要全量加載,為了提高頁(yè)面性能吼拥,往往需要按需加載資源倚聚。Domenic Denicola提案的動(dòng)態(tài)導(dǎo)入可以實(shí)現(xiàn)按需加載。這個(gè)類似函數(shù)的格式(不是繼承自Function .prototype)返回一個(gè)很強(qiáng)大的promise凿可。使用場(chǎng)景比如: 按需導(dǎo)入惑折,在一個(gè)腳本中計(jì)算模塊名并加載執(zhí)行變得可能。
element.onclick = () =>{
import("/js/helpers.js")
.then((module) =>{
console.log(module)
})
.chatch((err) => {
// load err
console.log(err)
})
}
因此我們也可以使用async異步函數(shù)來(lái)配置處理
element.onclick = async () =>{
let module = await import("/js/helpers.js")
// 處理 導(dǎo)入module模塊
}
4. BigInt
JavaScript 中 Number 類型都保存為 64 位浮點(diǎn)數(shù)枯跑,精確度只能到 53 位,也就是說(shuō)Js 中 Number類型只能安全的表示-(2^53-1)至 2^53-1 范的值惨驶,超出這個(gè)范圍的整數(shù)計(jì)算或者表示會(huì)丟失精度。
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
且無(wú)法正確表示大于或等于 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中的一個(gè)限制粗卜。BigInt是一個(gè)能表示任意精度整數(shù)的新基礎(chǔ)類型。你可以通過(guò)使用BigInt方法或者在一個(gè)數(shù)字后添加n后綴來(lái)把一個(gè)數(shù)字轉(zhuǎn)換為一個(gè)新的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遺留了一個(gè)關(guān)于for-in循環(huán)順序的詳細(xì)描述。在 ECMA-262 5rd Edition 中對(duì)遍歷機(jī)制又進(jìn)行了調(diào)整焕数,并未并且規(guī)定具體的規(guī)則纱昧,不同瀏覽器有不同的實(shí)現(xiàn),這導(dǎo)致對(duì)屬性的遍歷順序存在不一致的問(wèn)題堡赔。ES2020 中要求對(duì)象的遍歷實(shí)現(xiàn)上识脆,各瀏覽器要保持一致。
6. 可選鏈(Optional chaining)
以前在處理多層對(duì)象屬性值獲取的時(shí)候,通常需要對(duì)各層級(jí)的屬性進(jìn)行校驗(yàn).
例如
let name = user && user.info && user.info.name
這是一種丑陋但又不得不做的前置校驗(yàn)善已,否則很容易命中 Uncaught TypeError: Cannot read property… 這種錯(cuò)誤灼捂,這極有可能讓你整個(gè)應(yīng)用掛掉。
對(duì)此换团,ES2020 進(jìn)行了優(yōu)化悉稠,可以通過(guò) ?. 來(lái)簡(jiǎn)化校驗(yàn)。
?. 操作符與 . 類似啥寇,兩者的區(qū)別在于偎球,?. 在獲取對(duì)象屬性時(shí)洒扎,如果其引用對(duì)象為 null 或 undefined辑甜,則表達(dá)式會(huì)發(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)常會(huì)遇到給某個(gè)變量或者對(duì)象的屬性添加默認(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 新增了更簡(jiǎn)潔的空值合并操作符(??)磷醋,左側(cè)值為 null 或 undefined 時(shí)返回右側(cè)默認(rèn)值
const name = user.name ?? "默認(rèn)名稱"
但對(duì)于邏輯或操作符來(lái)說(shuō),''胡诗、0 都會(huì)轉(zhuǎn)化為 false邓线,容易產(chǎn)生邏輯錯(cuò)誤淌友。在業(yè)務(wù)上,大多是是想判斷變量是否為 undefined 或者 null骇陈。
因此要注意,
// user.name的值會(huì)有可能會(huì)進(jìn)行隱式類型轉(zhuǎn)行,為0的時(shí)候,也會(huì)只用默認(rèn)值
const name = user.name || "默認(rèn)名稱" ;
// ES2020新增的?? 運(yùn)算符 有且僅當(dāng)user.name的為null或者undefined時(shí)
// 才會(huì)啟用默認(rèn)值
const name = user.name ?? "默認(rèn)名稱"