10分鐘學會 optioanl chaining 可選鏈
前言
說起來,可選鏈操作符( optional chaining
)都加入 babel
套餐很久了,筆者還是沒有在實踐中大量使用,第一個是對這個特性還是半吊子的理解,第二個是沒養成這個習慣~
這裡列舉一下常用的場景,以供下次能在專案實踐中更好的使用。
可選鏈操作符介紹
由於 JavaScript 是一門動態語言,所以我們訪問物件屬性的時候總是會很小心的做一系列的防禦性程式設計。
比如我們要訪問物件的某個屬性,但是物件是後端介面返回的,有可能為 null
,這時候我們在訪問屬性的時候一般會這樣寫 obj.key
,但是如果 obj
為 null
,那麼這時候你就會得到瀏覽器給你的一個錯誤提示,如下 ?。
在日常編碼中這種操作其實也很常見,我們一般這樣編碼避免產生這種錯誤
obj && obj.name
。但這樣屬性是一層的還好,但某些極端的場景下,你的資料層級是很深的,比如
obj.a.b.c.d.name
。沒有可選鏈之前,我們會使用一些第三方的庫函式去解決這個問題。
- lodash.get
- 使用 es6 解構(也需要一層層的剝開去寫,深層解構依然煩瑣)
- 自己實現 safeGet,或者學習 you-dont-need-lodash 裡面的 get 方法 -> 程式碼出處
const get = (obj,path,defaultValue = undefined ) => {
const travel = regexp =>
String.prototype.split
.call(path,regexp)
.filter(Boolean)
.reduce((res,key) => (res !== null && res !== undefined ? res[key] : res),obj);
const result = travel(/[,[\]]+?/) || travel(/[,[\].]+?/);
return result === undefined || result === obj ? defaultValue : result;
};
複製程式碼
有了可選鏈之後呢?
先來看一個栗子?.
const obj = { a: { b: [{ name: 'obj' }] } }
// 原本的寫法
console.log(obj && obj.a && obj.a.b.length && obj.a.b[0].name)
// 可選鏈寫法
console.log(obj?.a?.b?.[0]?.name); // obj
console.log(obj?.b?.c?.d) // undefined
複製程式碼
大大的簡化了判斷,用起來可是
這裡如果 ?. 判斷的物件是 nullish(要麼是
undefined
,要麼是 null
) 值的話。表示式就會短路(不在往後執行),返回 undefined
如果配合空值合併運算符用起來~
const obj = { a : { name: 'obj'} }
obj?.a?.b ?? 'hello world' // hello world
複製程式碼
這裡也有要注意的點,空值合併運算子只有左邊是 nullish
的時候,才會返回右邊的表示式。
而我們以前經常是有的邏輯或 ||
操作,則是如果左邊為假值,就返回右邊的表示式。
如何在專案中使用
Node
Node.js 的話,可以在 v14.0.0 或以上版本獲得原生支援。
前端專案
可以藉助 babel 外掛 來體驗。
安裝
yarn add @babel/plugin-proposal-optional-chaining --dev
複製程式碼
使用
{
"plugins": ["@babel/plugin-proposal-optional-chaining"]
}
複製程式碼
Vue 模板中
很遺憾,暫不支援~看尤大的了。
基本使用
讀取物件屬性
const obj = { a: { name: 'obj' } }
obj?.a?.name // obj
複製程式碼
讀取物件屬性,動態 key
const obj = { a: { name: 'obj' } }
obj?.a?.['name'] // obj
複製程式碼
陣列下標訪問
const skills = ['CSS','HTML','JavaScript','Vue']
skills?.[3] // Vue
複製程式碼
函式呼叫
const obj = {
fun() {
console.log('hello world')
}
}
obj?.fun?.() // hello world
複製程式碼
進階使用
代替三元運算子
foo ? foo.bar : defaultValue // before
foo?.bar ?? defaultValue // after
複製程式碼
簡化正則表示式取值
// before
let match = "#C0FFEE".match(/#([A-Z0-9]+)/i)?.[1]
let hex = match && match[1]
// after
let hex = "#C0FFEE".match(/#([A-Z0-9]+)/i)?.[1]
複製程式碼
屬性檢查
// before
if (element.prepend) element.prepend(otherElement);
// after
element?.prepend(otherElement)
複製程式碼
避免過度使用
我們雖然有了錘子?,但是不能看到什麼都看作是釘子。
if 判斷之後需要執行多個操作
// before
if (foo) {
something(foo.bar);
somethingElse(foo.baz);
andOneLastThing(foo.yolo);
}
// after
something(foo?.bar);
somethingElse(foo?.baz);
andOneLastThing(foo?.yolo);
複製程式碼
這裡重構後的程式碼,我們會從原來只會判斷一次,變成需要判斷三次...而且沒增加一個屬性,一個方法又要再判斷一次。
所以也不能隨便亂用啊,親!
省去賦值判斷
// before
if (foo && foo.bar) {
foo.bar.baz = someValue;
}
// after
foo?.bar?.baz = someValue
複製程式碼
看到這程式碼,是不是以為是個常規正常操作?
執行一下
很明顯的,一個執行錯誤狠狠的甩在了我們的臉上~
所以可選鏈雖好,也不能亂用呀!
使用陷阱
判斷嚴格相等
首先我們來看一段程式碼
if (foo?.bar === baz) {
// todo
}
複製程式碼
看了看,好像沒什麼問題啊?
因為隱藏的
Bug
還得滿足條件才會觸發 (不過可能有些朋友會說,那不是 Bug
,而是我留下的彩蛋.....)- 當
foo
不存在的時候,可選連結串列達式會返回undefined
- 當
baz
變數的值為undefined
的時候
恭喜你,邏輯被運行了,同理還有判斷不等於的時候也會有這種問題。
判斷非嚴格相等
先看程式碼
if (foo?.bar !== SomeValue) {
// TODO
}
複製程式碼
上面說了,如果可選連結串列達式短路會返回一個 undefined
,那這時候,如果 someValue
是一個其他值的話,那你可能也在你自己程式碼裡面埋下了個彩蛋..
舉個例子 undefined !== 'string'
...
結語
本篇文章介紹了什麼是可選鏈,以及基本語法,和列舉了部分使用場景,相信這麼聰明的你肯定已經學會了!
下次再遇到後端丟過來的深層巢狀物件也不怕啦~