可選鏈 (Optional chaining)
可選鏈 讓我們在查詢具有多層級的對象時(shí),不需要再進(jìn)行冗余的各種前置校驗(yàn)
例如日常開發(fā)中,我們經(jīng)常會遇到這種查詢
let name = user && user.info && user.info.name;
又或者是這種
let age = user && user.info && user.info.getAge && user.info.getAge();
如果在任何級別的對象中都有 undefined 或者 null 的嵌套對象,如果不進(jìn)行檢查,那么很容易命中 Uncaught TypeError: Cannot read property... 這種錯(cuò)誤,這很有可能讓你的程序崩潰。因此,我們必須檢查每個(gè)級別,以確保當(dāng)它遇到 undefined 或 null 對象時(shí)不會崩潰。
使用可選鏈運(yùn)算符,只需要使用 ?. 來訪問嵌套對象。而且如果碰到 undefined 或 null 屬性,那么他只會返回 undefined。
使用可選鏈,上面的代碼可改為:
let name = user?.info?.name;
let age = user?.info?.getAge?.();
可選鏈中的 ? 表示如果聞號左邊的表達(dá)式有值,就會繼續(xù)查詢問好后面的字段
空值合并運(yùn)算符 (Nullish coalescing Operator)
undefined 或 null 值所產(chǎn)生的另一個(gè)問題,如果我們想要的變量為 undefined 或 null,則必須給變量設(shè)置默認(rèn)值。例如:
const a = b || '暫無數(shù)據(jù)';
當(dāng)使用 || 運(yùn)算符將 b 賦值給 a 時(shí),如果 b 被定義為 undefined,我們就必須設(shè)置一個(gè)默認(rèn)值。
運(yùn)算符 || 的問題在于,在 JS 中,所有類似于 0、false 或空字符串之類的值,在進(jìn)行邏輯操作符判斷時(shí),都會自動(dòng)轉(zhuǎn)化為 false,如果用戶輸入的本身就是 0,a 也會被賦值為 暫無數(shù)據(jù),就會出現(xiàn)邏輯錯(cuò)誤。
為了解決這個(gè)問題,創(chuàng)建了 nullish 合并運(yùn)算符,用 ?? 表示。有了它,我們僅在第一項(xiàng)為 null 或 undefined 時(shí)設(shè)置默認(rèn)值。
使用無效的合并運(yùn)算符,以上表達(dá)式可改為:
const a = b ?? '暫無數(shù)據(jù)';
通過 # 給 class 添加私有變量
class Counter {
#num = 10;
increment() {
this.#num ++;
}
getNum() {
return this.#num;
}
}
const counter = new Counter()
counter.increment()
console.log(counter.getNum()) // 11
console.log(counter.#num) // SyntaxError
在 ES2020 中,通過 # 可以給 class 添加私有變量。在 class 的外部我們無法獲取該值。這樣就不需要使用閉包來隱藏不想暴露給外籍的私有變量。
BigInt
Js 中 Number 類型只能安全地表示 -(2^53-1) 至 2^53-1 范圍內(nèi)的值,即 Number.MIN_SAFE_INTEGER 至 Number.MAX_SAFE_INTEGER, 超出這個(gè)范圍的整數(shù)計(jì)算或者表示會丟失精度。
let num = Number.MIN_SAFE_INTEGER; // 9007199254740991
num += 1; // 9007199254740992
// 再加 +1 后無法正常運(yùn)算
num += 1; // 9007199254740992
// 兩個(gè)不同的值比較,返回 true
console.log(9007199254740992 === 9007199254740993); // true
為了解決這個(gè)問題,ES2020 提供了一種新的數(shù)據(jù)類型:BigInt。
使用 BigInt 有兩種方式:
- 在整數(shù)字面量后面加
nconst bigIntNum = 9007199254740993n; - 使用
BigInt函數(shù)
通過 BigInt,可以安全地進(jìn)行大數(shù)整型計(jì)算const bigIntNum = BigInt(9007199254740993) const anotherBigIntNum = BigInt('9007199254740995')const bigNumRet = 9007199254740993n + 9007199254740993n; bigNumRet.toString(); // "18014398509481986"
注意:
- BigInt 是一種新的數(shù)據(jù)原始類型
typeof 9007199254740993n; // 'bigint'; - 盡可能避免通過調(diào)用函數(shù)
BigInt方式來實(shí)例化超大整型。因?yàn)閰?shù)的字面量實(shí)際也是 Number 類型的一次實(shí)例化,超出安全范圍的數(shù)字,可能會引起精度丟失。
Promise.allSettled
Promise.all 缺陷
Promise.all 具有并發(fā)執(zhí)行異步任務(wù)的能力,但是它最大的問題就是如果其中某個(gè)任務(wù)出現(xiàn)異常(reject),所有任務(wù)都會掛掉,Promise 直接進(jìn)入 reject 狀態(tài)。
Promise.all([
Promise.reject({ code: 500, message: ''服務(wù)異常 }),
Promise.resolve({ code: 200, list: [] }),
Promise.resolve({ code: 200, list: [] }),
]).then(res => {
// 如果其中一個(gè)任務(wù)是 reject,則不會執(zhí)行到這個(gè)回調(diào)
RenderContent(res)
}).catch(error => {
// 本例中會執(zhí)行到這個(gè)回調(diào)
// error: { code: 500, message: '服務(wù)異常' }
})
我們需要一種機(jī)制,如果并發(fā)任務(wù)中,無論一個(gè)任務(wù)正?;蛘弋惓?,都會返回對應(yīng)的狀態(tài)(fulfilled 或者 rejected)與結(jié)果(業(yè)務(wù) value 或者 拒因 reason),在 then 里面通過 filter 來過濾出想要的業(yè)務(wù)邏輯結(jié)果,這就能最大限度的保障業(yè)務(wù)當(dāng)前狀態(tài)的可訪問性,而 Promise.allSettled 就是解決這個(gè)問題的。
Promise.allSettled(iterable)
iterable:一個(gè)可迭代的對象,例如 Array,其中每個(gè)成員都是 Promise
返回一個(gè)在所有給定的 promise resolved 或者 rejected 的 promise,并帶有一個(gè)對象數(shù)組,每個(gè)對象表示對應(yīng)的 promise 結(jié)果。
Promise.allSettled([
Promise.reject({ code: 500, message }),
Promise.resolve({ code: 200, list: [] }),
Promise.resolve({ code: 200, list: [] }),
]).then(res => {
/*
[
{ status: "rejected", reason: {...} },
{ status: "fulfilled", value: {...} },
{ status: "fulfilled", value: {...} },
]
*/
// 過濾掉 reject 狀態(tài),盡可能多的保證頁面區(qū)域數(shù)據(jù)渲染
RenderContent(res.filter(item => {
return item.status !== 'rejected';
}));
})
dynamic-import
靜態(tài) import 和動(dòng)態(tài) import() 有各自的特點(diǎn)和使用場景。
static import 是沒有括號的,dynamic import() 是帶括號的。
用法區(qū)別:
// 有聲明提升,一般只放在頭部位置
static import: import xxx from 'xxx';
// 可以放在任何位置
dynamic import(): const xxx = import('xxx');
按需執(zhí)行邏輯資源都體現(xiàn)在某一個(gè)時(shí)間回調(diào)中去加載,為了首屏渲染速度更快,很多時(shí)候都是按需加載。
el.onclick = () => {
import('/example.js').then(module => {
module.todo();
}).catch(err => {
// load error
})
}
globalThis
JavaScript 在不同的環(huán)境獲取全局對象有不同的方式,node 中通過 global,web 中通過 window、self 等,有些甚至通過 this 獲取,但是通過 this 是極其危險(xiǎn)的,this 在 js 中很依賴當(dāng)前的執(zhí)行上下文。
globalThis 提供了標(biāo)準(zhǔn)的方式去獲取不同環(huán)境下的全局對象。它不想 window 或者 self 這些屬性,而是確??梢栽儆袩o窗口的環(huán)境下都可以正常工作,不必?fù)?dān)心它的運(yùn)行環(huán)境。
- 過去獲取全局對象的方式,可通過一個(gè)全局函數(shù)
const getGlobal = () => { if (typeof self !== 'undefined') return self; if (typeof window !== 'undefined') return window; if(typeof global !== 'undefined') return global; throw new Error('unable to locate global object'); } const globals = getGlobal(); console.log(globals) - 使用
globalThis獲取全局對象console.log(globalThis)