在JavaScript中長的鏈式調用可能容易出錯,由於任何一步均可能出現null
或undefined
(也被稱爲「無效」值)。檢查每一個步驟的屬性是否存在很容易變成深層次嵌套的if
聲明或者複製屬性訪問鏈的長的if
條件:javascript
// 容易出錯的版本,可能拋出錯誤
const nameLength = db.user.name.length;
// 不容易出錯,可是難以閱讀
let nameLength;
if (db && db.user && db.user.name)
nameLength = db.user.name.length;
複製代碼
以上還能夠使用三元表達式,可是一樣難以閱讀:java
const nameLength =
(db
? (db.user
? (db.user.name
? db.user.name.length
: undefined)
: undefined)
: undefined);
複製代碼
咱們並不想寫出這樣的代碼,因此有一些代替方案是可取的。一些語言(例如swift,具體查看https://www.jianshu.com/p/5599b422afb0)針對這個問題提供了優雅的解決方案——可選調用鏈。 根據最近的規範,「可選調用鏈是一個或多個屬性訪問和函數調用的鏈,以?.
開頭」。git
使用新的可選調用鏈,咱們能夠重寫上面的demo:github
// 依然檢查錯誤,可是可讀性更高
const nameLength = db?.user?.name?.length;
複製代碼
使用可選調用鏈,當db
,user
,或name
是undefined
或者null
的時候,nameLength
被初始化爲undefined
,而不是像以前那樣拋出錯誤。數據庫
**Note:**可選調用鏈比咱們本身用if(db && db.user && db.user.name)
檢查更加健壯,例如,若是name
是一個空字符串,可選字符串會將name?.length
改成name.length
而後獲得正確的長度0
,可是若是像咱們以前那樣作判斷,不會獲得正確的值,由於在if語句中空字符和false
的行爲相同。可選調用鏈修復了這個常見的bug。swift
還有一個用於調用可選方法的運算符:數組
// 使用可選方法擴展接口,僅適用於管理員用戶
const adminOption = db?.user?.validateAdminAndGetPrefs?.().option;
複製代碼
這個語法可能有點兒出乎意料,由於這裏的運算符是?.()
,該運算符適用於以前的表達式。安全
可選調用鏈還有第三種用法,便可選動態屬性訪問?.[]
。若是對象中有該key對應的value,則返回value,不然返回undefined
。demo以下:函數
// 使用動態屬性名訪問屬性對應的值
const optionName = 'optional setting';
const optionLength = db?.user?.preferences?.[optionName].length;
複製代碼
該用法一樣適用於可選索引數組,例如:ui
// 若是`userArray`是`null`或`undefined`,則`userName`被優雅的賦值爲`undefined`
const userIndex = 42;
const userName = usersArray?.[userIndex].name;
複製代碼
可選調用鏈能夠和nullish coalescing ?? 操做符結合使用,返回一個非undefined
的默認值。這樣能夠使用指定的默認值安全的進行深層屬性訪問,解決了以前用戶須要JavaScript庫才能解決的問題,例如lodash的_.get:
const object = { id: 123, names: { first: 'Alice', last: 'Smith' }};
{ // With lodash:
const firstName = _.get(object, 'names.first');
// → 'Alice'
const middleName = _.get(object, 'names.middle', '(no middle name)');
// → '(no middle name)'
}
{ // With optional chaining and nullish coalescing:
const firstName = object?.names?.first ?? '(no first name)';
// → 'Alice'
const middleName = object?.names?.middle ?? '(no middle name)';
// → '(no middle name)'
}
複製代碼
可選調用鏈有一些有趣的屬性:短路(short-circuiting),堆疊(stacking)和可選刪除(optonal deletion)。下面經過例子來了解這些屬性。
*短路(short-circuiting)*即若是可選調用鏈提早返回則不計算表達式的其他部分:
// 只有在`db`和`user`不是`undefined`的狀況下`age`纔會+1
db?.user?.grow(++age);
複製代碼
*堆疊(stacking)*意味着能夠在一系列屬性訪問中應用多個可選調用運算符:
// 一個可選鏈能夠跟隨另外一個可選鏈
const firstNameLength = db.users?.[42]?.names.first.length;
複製代碼
可是,考慮在一個鏈中使用多個可選調用運算符。若是一個value保證是有效的,那麼不鼓勵使用?.
去訪問屬性。像在以前的例子中,db
必然是定義的,可是db.users
和db.users[42]
多是未定義的。若是數據庫中有這樣的用戶,那麼name.first.length
也是始終被定義的。
*可選刪除(optonal deletion)*就是delete
操做符能夠和可選鏈一塊兒使用:
// 當且僅當`db`是定義過的時候刪除`db.user`
delete db?.user;
複製代碼
更多細節能夠訪問該提案的語義部分。
原文連接:v8.dev/features/op…