日常業務中,經常會遇到這么一個情況:需要用到的某個屬性在接口返回的數據中可能是不存在的或者其上一級的屬性是不存在的,例如:
1 const zs = { 2 info : { 3 name : { 4 firstName : "張", 5 lastName : "三" 6 }, 7 } 8 } 9 zs.info.name.firstName // 張
如果數據是齊全的情況下,通過 zs.info.name.firstName可以正常獲取到“張”,此時如果name這個字段沒有,而直接訪問這個字段就會報一個非常常見的錯誤:Cannot read property 'xxx' of undefined,意思是無法在undefined上讀取屬性"xxx"。
我們在日常解決這個問題比較安全的寫法是:
(zs && zs.info && zs.info.name && zs.info.name.firstName) || "default"
上面判斷了四次,看每一層是否有值,
或者也可以用三目運算也可以判斷.
ES2020引入了“鏈判斷運算符”(?.),簡化了上面的寫法
1 const zs = { 2 info : { 3 name : { 4 firstName : "張", 5 lastName : "三" 6 }, 7 } 8 } 9 10 const fn = zs?.info?.name?.lastName || "四"
說明:
- ?.運算符直接在鏈式調用的時候判斷,左側的對象是否為null或undefined,是的話不再往下運算,而是返回undefined
或者可以用來判斷對象中是否有某個方法:
1 const o = { 2 add: () => { console.log(123) } 3 } 4 o.add?.() // 123
上面代碼中,o.add方法如果定義了,就會調用該方法,否則,不再執行?.后面的部分
鏈判斷運算符有三種用法:
- obj?.prop // 對象屬性
- obj?.[expr] // 同上
- func?.(...args) // 函數或對象方法的調用
obj?.[expr]的一個例子:
1 var str="The rain in SPAIN stays mainly in the plain" 2 let h = str.match(/ain/)?.[1]
上面代碼中,判斷是否有匹配/ain/正則,如果匹配h 等於匹配的字符串。否則等於undefined
以下是?.運算符常見形式,以及不適用該運算符時的等價形式
a?.b // 等同於 a == null ? undefined : b a?.[x] // 等同於 a == null ? undefined : a[x] a?.b() // 等同於 a == null ? undefined : a.b() a?.() // 等同於 a == null ? undefined : a()
說明:后面兩種形式,如果a?.b()里面的a.b()不是函數,不可調用,會報錯。a?.()也是這樣
使用鏈判斷運算符需要注意的事項:
(1)短路機制
?.運算符相當於一種短路機制,只要不滿足條件,就不再往下執行
(2)delete運算符
delete a?.b // 等同於 a == null ? undefined : delete a.b
上面代碼中,如果a是undefined或null,會直接返回undefined,而不會進行delete運算
(3)括號的影響
如果屬性鏈有圓括號,鏈判斷運算符對圓括號外部沒有影響,只對圓括號內部有影響
(a?.b).c // 等價於 (a == null ? undefined : a.b).c
上面代碼中,?.對圓括號外部沒有影響,不管a對象是否存在,圓括號后面的.c總是會執行。
一般來說,使用?.運算符的場合,不應該使用圓括號
(4)報錯場合
以下寫法是禁止的,會報錯
// 構造函數 new a?.() new a?.b() // 鏈式判斷運算符的右側有模板字符串 a?.`${b}` a?.b`${c}` // 鏈判斷運算符的左側是super super?.() super?.foo // 連運算符用於賦值運算符左側 a?.b = c
(5)右側不得為十進制數值
為了保證兼容以前的代碼,允許foo?.3:0
被解析成foo ? .3 : 0
,因此規定如果?.
后面緊跟一個十進制數字,那么?.
不再被看成是一個完整的運算符,而會按照三元運算符進行處理,也就是說,那個小數點會歸屬於后面的十進制數字,形成一個小數。