- 原文地址:Nested Ternaries are Great
- 原文做者:Eric Elliott
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:yoyoyohamapi
- 校對者:StarrieC goldEli
(譯註:該圖是用 PS 將煙霧處理成方塊狀後獲得的效果,參見 flickr。)javascript
這是 「軟件編寫」 系列文章的第十四部分,該系列主要闡述如何在 JavaScript ES6+ 中從零開始學習函數式編程和組合化軟件(compositional software)技術(譯註:關於軟件可組合性的概念,參見維基百科前端
過去的經驗會讓你相信,嵌套三元表達式是不可讀的,應當儘可能避免。android
經驗有時候是愚蠢的。ios
真相實際上是,三元表達式一般比 if 語句更加簡單。人們不相信的緣由有兩個:git
在咱們深刻細節以前,先爲三元表達式作一個定義:github
一個三元表達式是一個進行求值的條件表達式。它由一個條件判斷,一個真值子句(truthy value,當條件爲真時返回的值)和一個假值子句(falsy clause,當條件爲假時返回的值)構成。express
它們就像下面這樣:編程
(conditional)
? truthyClause
: falsyClause
複製代碼
一些編程語言(包括 Smalltalk、Haskell 以及大多數函數式編程語言)都沒有 if 語句。取而代之的是,if
表達式。後端
一個 if 表達式就是進行求值的條件表達式。它由一個條件判斷,一個真值子句(truthy value,當條件爲真時返回的值)和一個假值子句(falsy clause,當條件爲假時返回的值)構成。
這個定義看起來是否是很熟悉?大多數函數式編程語言都使用三元表達式來表示 if
關鍵字。這是爲何呢?
一個表達式是一個求取單值的代碼塊。
一條語句則是一個不必定進行求值的代碼段。在 JavaScript 中,一個 if 語句不會進行 求值。爲了讓 JavaScript 中的 if 語句有用,就必須引發一個反作用(side-effect)或者是在 if 語句包裹的代碼塊中返回一個值。
在函數式編程中,咱們試圖避免可變性以及其餘的反作用。因爲 JavaScript 中的 if
先天會帶來可變性和反作用,因此包括我在內的一些函數式編程的擁躉會使用三元表達式來替換它。
三元表達式的思惟模式與 if 語句有所不一樣,但若是你嘗試實踐幾周,你也會天然而然的轉到三元表達式。這可不僅是由於它縮小了代碼量,另外一些優點你也將在後文中看到。
我最常聽到的關於三元表達式的抱怨就是 「難於閱讀」。讓咱們經過一些代碼範例來粉碎謠言:
const withIf = ({
conditionA, conditionB
}) => {
if (conditionA) {
if (conditionB) {
return valueA;
}
return valueB;
}
return valueC;
};
複製代碼
注意到,這個版本中,經過嵌套的條件和可見的括號來分離真值和假值,這讓真假值看起來失去了聯繫。這只是一個很簡單的邏輯,但卻須要花力氣理解。
讓咱們再看看一樣的邏輯,用三元表達式是怎麼完成的:
const withTernary = ({
conditionA, conditionB
}) => (
(!conditionA)
? valueC
: (conditionB)
? valueA
: valueB
);
複製代碼
這兒一些值得分享和討論的點:
譯註:菊鏈法(Daisy Chaining),原意指多個設備按序或者圍城一環進行鏈接。
首先,咱們已經將嵌套鋪平了。「嵌套的」 三元表達式有點用詞不當,由於三元表達式很容易經過一條直線進行撰寫,你徹底不須要使用不一樣的縮進來嵌套它們。它們容易按照直線的順序,自頂向下地進行閱讀,一旦知足了某個真值或者假值就會當即返回。
若是你正確的書寫了三元表達式,也就不須要解析任何的嵌套。沿着一條直線走,很難迷路。
咱們應該稱其爲 「鏈式三元表達式」 而不是 「嵌套三元表達式」。
還有一點我想指出的就是,爲了簡化直線連接,我對順序稍做了更改:若是你到達了三元表達式末尾,而且發現你須要寫兩條冒號子句(:
):
// 譯者補充這種狀況
const withTernary = ({
conditionA, conditionB
}) => (
conditionA
? conditionB
? valueA
: valueB
: valueC
)
複製代碼
將最後一條子句提到鏈頭,而且反轉第一個條件判斷邏輯來簡化三元表達式的解析。如今,再也不有任何困惑了!
值得注意的是,咱們可使用一樣的手段來簡化 if 語句:
const withIf = ({
conditionA, conditionB
}) => {
if (!conditionA) return valueC;
if (conditionB) {
return valueA;
}
return valueB;
};
複製代碼
這好不少了,可是 conditionB
的關聯子句被打破仍然是可見的,這仍是會形成困惑。在維護代碼期間,我已經看到過相似問題引發了邏輯上的 bug。即使邏輯被打平,這個版本的代碼相較於三元表達式版本,仍是稍顯混亂。
if
版本的代碼含有許多噪聲:if
關鍵字 vs ?
,使用 return
來強制語句返回一個值、額外的分號、額外的括號等等。 不一樣於本文的例子,大多數 if 語句也改變了外部狀態,這不只增多了代碼,還提升了代碼複雜度。
這些額外代碼帶來的負面影響是我不喜歡用 if 的重要緣由之一。在此以前,我已經討論過,但在每一個開發者都瞭然於胸前,我仍是願意不厭其煩地再嘮叨一下:
平均下來,人類大腦只有一小部分共享資源提供給對於離散的存儲在工做記憶中的量子,而且每個變量潛移默化地消費這些量子。當你添加更多的變量,你就會喪失對這些變量的含義的精確記憶能力。典型的,工做記憶模型涉及 4-7 個離散兩字。超過這個數,錯誤率就會陡增。
與三元表達式相反,咱們不得不將可變性和反作用融入 if 語句中,這一般會形成添加一些本不須要的變量進去。
簡練的代碼也會提升你代碼的信噪比。這就像在聽收音機 —— 若是收音機沒有正確的調到某個頻道,你就會聽到許多幹擾噪聲,所以很難聽到音樂。當你正確的調到某個頻道,噪聲就會遠離,你聽到的將是強烈的音樂信號。
代碼也相似。表達式越精簡,則越容易理解。一些代碼給了咱們有用的信息,一些則讓咱們雲裏霧裏。若是你能夠在不丟失所要傳達的信息的前提下,縮減了代碼量,你將會讓代碼更易於解析,也讓其餘的開發者更容易理解當中的意思。
看一眼函數的前先後後。這就好像函數進行了節食,減去了成噸的體重。這是很是重要的,由於額外的代碼就意味着更大的藏匿 Bug 的表面積,也就意味着更多的 bug。
更少的代碼 = 更小的藏匿 bug 的表面積 = 更少的 bug。
許多 if 語句不僅進行了求值。它們也形成了反作用,或是改變了狀態,假若你想要知道 if 語句的完整影響,就須要知道 if 語句中的反作用的影響,對共享狀態全部的變動歷史等等。
將你本身限制到返回一個值,能強制你遵循這個原則:切斷依賴將以讓你的程序更易於理解、調試、重構以及維護。
這確實就是三元表達式中我最喜歡的益處:
使用三元表達式將讓你成爲更好的開發者。
因爲全部的三元表達式都易於使用一個直線來自頂向下地分配,所以,稱其爲 「嵌套的三元表達式」 有點用詞不當。取而代之的是,咱們稱其爲 「鏈式三元表達式」。
相較於 if 語句,鏈式三元表達式有若干的優點:
視頻課程和函數式編程已經爲 EricElliottJS.com 的網站成員準備好了。若是你還不是當中的一員,如今就註冊吧。
Eric Elliott 是 「編寫 JavaScript 應用」 (O’Reilly) 以及 「跟着 Eric Elliott 學 Javascript」 兩書的做者。他爲許多公司和組織做過貢獻,例如 Adobe Systems、Zumba Fitness、The Wall Street Journal、ESPN 和 BBC 等 , 也是不少機構的頂級藝術家,包括但不限於 Usher、Frank Ocean 以及 Metallica。
大多數時間,他都在 San Francisco Bay Area,同這世上最美麗的女子在一塊兒
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。