最近工做中發現團隊有些同窗不太瞭解
Optional chaining
和Nullish coalescing
兩個新的操做符,正好推廣一波shell
Optional chaining
和 Nullish coalescing
目前都已經歸入 ECMA-262
標準中,不過兼容性還差得遠,以下: json
babel
,兼容都不是問題。不過仍是要注意使用前必定要確認項目是否支持這倆操做符,切勿只顧一時爽,至於如何兼容能夠看下方。
Optional chaining
介紹Optional chaining
是爲了解決程序中鋪天蓋地的 Cannot read property 'foo' of undefined
錯誤或者是滿屏幕的 a && a.b && a.b.c && a.b.c.d
邏輯與運算符或者是三元操做符。 有了 Optional chaining
,咱們能夠十分優雅的去獲取某些可能不存在的數據。緩存
Optional chaining
操做符的定義是:當左操做數爲空值時(null
、undefined
)中斷取值操做並返回 undefined
。(方法調用爲方法爲空值時中斷調用並返回 undefined
)。babel
const foo = a?.b?.c?.d;
複製代碼
比起一長串的邏輯與運算符,不但優雅美觀,並且方便、可讀性更高,邏輯與有時候會偷懶不寫,不過自從用了 Optional chaining
,不再用偷懶了,屬性取值如此簡單穩定,不再怕屬性找不到了。ui
Optional chaining
有三種標準語法:spa
// 靜態屬性
a?.b?.c.d
// 動態屬性
a?.[b]?.c.d
// 方法調用
a?.b?.()
複製代碼
不過也須要注意 Optional chaining
後不能跟數字,由於存在語法上的重合。插件
a?.3:0;
a?.[3]
複製代碼
因此須要接數字時記得使用 []
。eslint
同時 Optional chaining
也支持 delete
:code
delete a?.b?.c;
複製代碼
注意上述操做不管 a.b
的值只會刪除 a.b.c
不會刪除 a.b
cdn
Nullish coalescing
介紹再來看看 Nullish coalescing
,Nullish coalescing
和 Optional chaining
算是一對好基友,主要用來作一些默認值的設置。
Nullish coalescing
操做符的定義是:當左操做數爲空值時(null
、undefined
)返回右操做數,不然返回左操做數。
const foo = a?.b?.c?.d ?? 'bar';
複製代碼
有的同窗可能好奇這不是和邏輯或同樣嗎?
const foo = a?.b?.c?.d || 'bar';
複製代碼
其實仍是不同的 Nullish coalescing
從名字能夠看出來:空值合併,也就是隻有左操做數爲空值時纔會應用右操做數,而邏輯或使用的是假值進行判斷,在一些邊界狀況下如左操做數爲 0
、''
空字符串時 Nullish coalescing
會更合理,能夠減小一些邊界值的判斷。
null ?? 'foo'; // 'foo'
undefined ?? 'foo'; // 'foo'
0 ?? 'foo'; // 0
'' ?? 'foo'; // ''
null || 'foo'; // 'foo'
undefined || 'foo'; // 'foo'
0 || 'foo'; // 'foo'
'' || 'foo'; // 'foo'
複製代碼
這兩個新的操做符其實如今已經包含在新版的 preset-env
中,若是你的項目 preset-env
較新的化,那恭喜🎉,你不須要作什麼額外的操做就能夠用上了。
不過若是是較舊版的 preset-env
,那麼須要安裝上相應的插件來進行啓用:
yarn add @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-nullish-coalescing-operator --dev
複製代碼
安裝完成後不要忘記在 babel
配置中啓用:
{
"plugins": ["@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator"]
}
複製代碼
順便看一下 babel
是如何轉義 Optional chaining
和 Nullish coalescing
的。
Optional chaining
的轉義
a?.b?.[c]?.()
delete a?.b?.c
// babel 轉義後 =====>
"use strict";
var _a, _a$b, _a$b$c, _a2, _a2$b;
(_a = a) === null || _a === void 0 ? void 0 : (_a$b = _a.b) === null || _a$b === void 0 ? void 0 : (_a$b$c = _a$b[c]) === null || _a$b$c === void 0 ? void 0 : _a$b$c.call(_a$b);
(_a2 = a) === null || _a2 === void 0 ? true : (_a2$b = _a2.b) === null || _a2$b === void 0 ? true : delete _a2$b.c;
複製代碼
細心的同窗能夠發現有幾個比較值得注意的點。
babel
會在每次屬性取值時將屬性值進行緩存而不是像平時代碼中常寫的直接 a && a.b && a.b.c
,這是爲了保證和原生實現的一致性,保證每一個屬性取值只會取一次,避免在一些 getter 屬性獲取時形成取值次數不一致差別性。babel
在判斷值是否爲空時並無直接使用 == null
而是使用了比較繁瑣的 === null || === void 0
,這個主要是爲了兼容 document.all
,關於 document.all
寫在後面。Nullish coalescing
的轉義
a ?? b
// babel 轉義後 =====>
"use strict";
var _a;
(_a = a) !== null && _a !== void 0 ? _a : b;
複製代碼
能夠注意到一樣是爲了兼容 document.all
,Nullish coalescing
也使用了 === null || === void 0
來進行判斷。
document.all
document.all
是一個比較奇怪的值,它是 document
中全部元素的集合,可是它是一個假值,而且是一個特殊的空值。
document.all || 1 // 1
document.all == null // true
document.all === null // false
document.all === undefined // false
複製代碼
document.all
是一個殘留的屬性,這些特性也是爲了一些之前的兼容考慮。HTML5 中已經將它廢棄,能夠不作過多瞭解。有興趣的能夠看下 MDN Document.all 的文檔。
loose
因爲 document.all
是一個廢棄的屬性,現實開發中其實不會遇到使用的場景,既然如此咱們就不必由於一個廢棄的屬性而致使 babel 轉義出大量無心義代碼。 咱們能夠經過啓用插件的 loose
屬性來實現:
{
"plugins": [["@babel/plugin-proposal-optional-chaining", {"loose": false}], ["@babel/plugin-proposal-nullish-coalescing-operator", {"loose": false}]]
}
複製代碼
若是是 preset-env
中集成的更簡單,直接將 preset-env
中的 loose
設爲 true
就好了。
再看下編譯的代碼:
a?.b?.[c]?.()
// babel 轉義後 =====>
"use strict";
var _a, _a$b, _a$b$c;
(_a = a) == null ? void 0 : (_a$b = _a.b) == null ? void 0 : (_a$b$c = _a$b[c]) == null ? void 0 : _a$b$c.call(_a$b);
複製代碼
a ?? b
// babel 轉義後 =====>
"use strict";
var _a;
(_a = a) != null ? _a : b;
複製代碼
代碼瞬間簡潔了許多。若是小心團隊有同窗誤用 document.all
也能夠在 eslint
中添加告警。
Optional chaining
和 Nullish coalescing
已經歸入標準一段時間了,使用後能夠大大增長屬性獲取、默認值設置等代碼的優雅、可讀性,減小各類 Cannot read property 'foo' of undefined
的錯誤狀況。等什麼,趕忙用起來吧。
若是以爲本文有用,請不要吝嗇您的贊👍