W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
最近新增的特性
這是一個(gè)最近添加到 JavaScript 的特性。 舊式瀏覽器可能需要 polyfills.
可選鏈 ?.
是一種訪問(wèn)嵌套對(duì)象屬性的安全的方式。即使中間的屬性不存在,也不會(huì)出現(xiàn)錯(cuò)誤。
如果你才剛開(kāi)始讀此教程并學(xué)習(xí) JavaScript,那可能還沒(méi)接觸到這個(gè)問(wèn)題,但它卻相當(dāng)常見(jiàn)。
舉個(gè)例子,假設(shè)我們有很多個(gè) user
對(duì)象,其中存儲(chǔ)了我們的用戶數(shù)據(jù)。
我們大多數(shù)用戶的地址都存儲(chǔ)在 user.address
中,街道地址存儲(chǔ)在 user.address.street
中,但有些用戶沒(méi)有提供這些信息。
在這種情況下,當(dāng)我們嘗試獲取 user.address.street
,而該用戶恰好沒(méi)提供地址信息,我們則會(huì)收到一個(gè)錯(cuò)誤:
let user = {}; // 一個(gè)沒(méi)有 "address" 屬性的 user 對(duì)象
alert(user.address.street); // Error!
這是預(yù)期的結(jié)果。JavaScript 的工作原理就是這樣的。因?yàn)?nbsp;user.address
為 undefined
,嘗試讀取 user.address.street
會(huì)失敗,并收到一個(gè)錯(cuò)誤。
但是在很多實(shí)際場(chǎng)景中,我們更希望得到的是 undefined
(表示沒(méi)有 street
屬性)而不是一個(gè)錯(cuò)誤。
……還有另一個(gè)例子。在 Web 開(kāi)發(fā)中,我們可以使用特殊的方法調(diào)用(例如 document.querySelector('.elem')
)以對(duì)象的形式獲取一個(gè)網(wǎng)頁(yè)元素,如果沒(méi)有這種對(duì)象,則返回 null
。
// 如果 document.querySelector('.elem') 的結(jié)果為 null,則這里不存在這個(gè)元素
let html = document.querySelector('.elem').innerHTML; // 如果 document.querySelector('.elem') 的結(jié)果為 null,則會(huì)出現(xiàn)錯(cuò)誤
同樣,如果該元素不存在,則訪問(wèn) null
的 .innerHTML
屬性時(shí)會(huì)報(bào)錯(cuò)。在某些情況下,當(dāng)元素的缺失是沒(méi)問(wèn)題的時(shí)候,我們希望避免出現(xiàn)這種錯(cuò)誤,而是接受 html = null
作為結(jié)果。
我們?nèi)绾螌?shí)現(xiàn)這一點(diǎn)呢?
可能最先想到的方案是在訪問(wèn)該值的屬性之前,使用 if
或條件運(yùn)算符 ?
對(duì)該值進(jìn)行檢查,像這樣:
let user = {};
alert(user.address ? user.address.street : undefined);
這樣可以,這里就不會(huì)出現(xiàn)錯(cuò)誤了……但是不夠優(yōu)雅。就像你所看到的,"user.address"
在代碼中出現(xiàn)了兩次。
我們看一個(gè)以相同方式獲取 document.querySelector
的例子:
let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;
我們可以看到用于進(jìn)行元素搜索的 document.querySelector('.elem')
在這里實(shí)際上被調(diào)用了兩次。這樣不優(yōu)雅。
對(duì)于嵌套層次更深的屬性,代碼會(huì)變得更丑,因?yàn)樾枰嗟闹貜?fù)。
例如,讓我們以相同的方式嘗試獲取 user.address.street.name
。
let user = {}; // user 沒(méi)有 address 屬性
alert(user.address ? user.address.street ? user.address.street.name : null : null);
這樣就太扯淡了,并且這可能導(dǎo)致寫(xiě)出來(lái)的代碼很難讓別人理解。
這里有一種更好的實(shí)現(xiàn)方式,就是使用 &&
運(yùn)算符:
let user = {}; // user 沒(méi)有 address 屬性
alert( user.address && user.address.street && user.address.street.name ); // undefined(不報(bào)錯(cuò))
依次對(duì)整條路徑上的屬性使用與運(yùn)算進(jìn)行判斷,以確保所有節(jié)點(diǎn)是存在的(如果不存在,則停止計(jì)算),但仍然不夠優(yōu)雅。
就像你所看到的,在代碼中我們?nèi)匀恢貜?fù)寫(xiě)了好幾遍對(duì)象屬性名。例如在上面的代碼中,user.address
被重復(fù)寫(xiě)了三遍。
這就是為什么可選鏈 ?.
被加入到了 JavaScript 這門(mén)編程語(yǔ)言中。那就是徹底地解決以上所有問(wèn)題!
如果可選鏈 ?.
前面的值為 undefined
或者 null
,它會(huì)停止運(yùn)算并返回 undefined
。
為了簡(jiǎn)明起見(jiàn),在本文接下來(lái)的內(nèi)容中,我們會(huì)說(shuō)如果一個(gè)屬性既不是 null
也不是 undefined
,那么它就“存在”。
換句話說(shuō),例如 value?.prop
:
value
?存在,則結(jié)果與 ?value.prop
? 相同,value
?為 ?undefined/null
? 時(shí))則返回 ?undefined
?。下面這是一種使用 ?.
安全地訪問(wèn) user.address.street
的方式:
let user = {}; // user 沒(méi)有 address 屬性
alert( user?.address?.street ); // undefined(不報(bào)錯(cuò))
代碼簡(jiǎn)潔明了,也不用重復(fù)寫(xiě)好幾遍屬性名。
這里是一個(gè)結(jié)合 document.querySelector
使用的示例:
let html = document.querySelector('.elem')?.innerHTML; // 如果沒(méi)有符合的元素,則為 undefined
即使 對(duì)象 user
不存在,使用 user?.address
來(lái)讀取地址也沒(méi)問(wèn)題:
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
請(qǐng)注意:?.
語(yǔ)法使其前面的值成為可選值,但不會(huì)對(duì)其后面的起作用。
例如,在 user?.address.street.name
中,?.
允許 user
為 null/undefined
(在這種情況下會(huì)返回 undefined
)也不會(huì)報(bào)錯(cuò),但這僅對(duì)于 user
。更深層次的屬性是通過(guò)常規(guī)方式訪問(wèn)的。如果我們希望它們中的一些也是可選的,那么我們需要使用更多的 ?.
來(lái)替換 .
。
不要過(guò)度使用可選鏈
我們應(yīng)該只將
?.
使用在一些東西可以不存在的地方。
例如,如果根據(jù)我們的代碼邏輯,
user
對(duì)象必須存在,但address
是可選的,那么我們應(yīng)該這樣寫(xiě)user.address?.street
,而不是這樣user?.address?.street
。
那么,如果
user
恰巧為 undefined,我們會(huì)看到一個(gè)編程錯(cuò)誤并修復(fù)它。否則,如果我們?yōu)E用?.
,會(huì)導(dǎo)致代碼中的錯(cuò)誤在不應(yīng)該被消除的地方消除了,這會(huì)導(dǎo)致調(diào)試更加困難。
?
?.
? 前的變量必須已聲明如果未聲明變量
user
,那么user?.anything
會(huì)觸發(fā)一個(gè)錯(cuò)誤:
// ReferenceError: user is not defined user?.address;
?.
前的變量必須已聲明(例如let/const/var user
或作為一個(gè)函數(shù)參數(shù))??蛇x鏈僅適用于已聲明的變量。
正如前面所說(shuō)的,如果 ?.
左邊部分不存在,就會(huì)立即停止運(yùn)算(“短路效應(yīng)”)。
因此,如果在 ?.
的右側(cè)有任何進(jìn)一步的函數(shù)調(diào)用或操作,它們均不會(huì)執(zhí)行。
例如:
let user = null;
let x = 0;
user?.sayHi(x++); // 沒(méi)有 "user",因此代碼執(zhí)行沒(méi)有到達(dá) sayHi 調(diào)用和 x++
alert(x); // 0,值沒(méi)有增加
可選鏈 ?.
不是一個(gè)運(yùn)算符,而是一個(gè)特殊的語(yǔ)法結(jié)構(gòu)。它還可以與函數(shù)和方括號(hào)一起使用。
例如,將 ?.()
用于調(diào)用一個(gè)可能不存在的函數(shù)。
在下面這段代碼中,有些用戶具有 admin
方法,而有些沒(méi)有:
let userAdmin = {
admin() {
alert("I am admin");
}
};
let userGuest = {};
userAdmin.admin?.(); // I am admin
userGuest.admin?.(); // 啥都沒(méi)發(fā)生(沒(méi)有這樣的方法)
在這兩行代碼中,我們首先使用點(diǎn)符號(hào)(userAdmin.admin
)來(lái)獲取 admin
屬性,因?yàn)槲覀兗俣▽?duì)象 userAdmin
存在,因此可以安全地讀取它。
然后 ?.()
會(huì)檢查它左邊的部分:如果 admin
函數(shù)存在,那么就調(diào)用運(yùn)行它(對(duì)于 userAdmin
)。否則(對(duì)于 userGuest
)運(yùn)算停止,沒(méi)有報(bào)錯(cuò)。
如果我們想使用方括號(hào) []
而不是點(diǎn)符號(hào) .
來(lái)訪問(wèn)屬性,語(yǔ)法 ?.[]
也可以使用。跟前面的例子類似,它允許從一個(gè)可能不存在的對(duì)象上安全地讀取屬性。
let key = "firstName";
let user1 = {
firstName: "John"
};
let user2 = null;
alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
此外,我們還可以將 ?.
跟 delete
一起使用:
delete user?.name; // 如果 user 存在,則刪除 user.name
我們可以使用 ?
?.
? 來(lái)安全地讀取或刪除,但不能寫(xiě)入可選鏈
?.
不能用在賦值語(yǔ)句的左側(cè)。
例如:
let user = null; user?.name = "John"; // Error,不起作用 // 因?yàn)樗谟?jì)算的是:undefined = "John"
可選鏈 ?.
語(yǔ)法有三種形式:
obj?.prop
? —— 如果 ?obj
?存在則返回 ?obj.prop
?,否則返回 ?undefined
?。obj?.[prop]
? —— 如果 ?obj
?存在則返回 ?obj[prop]
?,否則返回 ?undefined
?。obj.method?.()
? —— 如果 ?obj.method
? 存在則調(diào)用 ?obj.method()
?,否則返回 ?undefined
?。正如我們所看到的,這些語(yǔ)法形式用起來(lái)都很簡(jiǎn)單直接。?.
檢查左邊部分是否為 null/undefined
,如果不是則繼續(xù)運(yùn)算。
?.
鏈?zhǔn)刮覀兡軌虬踩卦L問(wèn)嵌套屬性。
但是,我們應(yīng)該謹(jǐn)慎地使用 ?.
,根據(jù)我們的代碼邏輯,僅在當(dāng)左側(cè)部分不存在也可接受的情況下使用為宜。以保證在代碼中有編程上的錯(cuò)誤出現(xiàn)時(shí),也不會(huì)對(duì)我們隱藏。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: