[譯] 支持 JavaScript 三元運算符

幾個月前,我在 Hacker News 上瀏覽到一篇(現已刪除)關於不要使用 if 語句的文章。若是你像我同樣對這個觀點還不太瞭解,那值得你去看一下。只要在 Hacker News 上搜索 「if 語句」,你就會看到一篇文章說:「你可能不須要 if 語句」,或者是稱 if 語句爲 「可疑代碼」,甚至是 「有害代碼」 的文章。聽着,你應該知道不一樣的編程思想都是值得尊重的,儘管他們宣稱使用 if 會傷害到別人。javascript

若是這對你來講還不夠,還有 「if 運動」。若是你加入,你會在網站上看到一個漂亮的橫幅,並且你的名字也會在上面。對!若是你加入的話,那會是多麼的有意思。css

當我第一次遇到這種奇怪的 「反對 if」 現象時,我以爲頗有趣,但可能只不過是一些人在網上發瘋了。你只須要谷歌一下,就能找到對任何事都瘋狂的人。好比這個討厭小貓的人。KITTENShtml

一段時間後,我看了 Linus Torvald 的 TED 演講。在那次演講中,他展現了兩張幻燈片。第一張幻燈片貼出了他認爲是 「bad taste」 的代碼。前端

第二個是相同功能的代碼,可是在 Linus 看來是 「good taste」。java

我意識到 Linus 是一個有點兩極化的人物,你可能不一樣意 「good taste」 與 「bad taste」 的描述。但我認爲,通常來講,第二張幻燈片對新手來講更容易理解。它簡潔、邏輯分支少且不包含 if 語句。我但願個人代碼應該是這樣的。它不必定是什麼高級的算法(永遠不會),但我但願它是邏輯簡潔的,還記得 Smashing Pumpkins 樂隊的 Billy Corgan 是如何描述的:linux

Cleanliness is godliness. And god is empty. Just like me.android

  • Billy Corgan, "Zero"

太可怕了!但這張專輯的確很棒。ios

除了讓代碼看起來雜亂以外,if 語句或 「分支邏輯」 還要求你的大腦同時去計算兩條獨立的邏輯路徑,以及這些路徑上可能發生的全部事情。若是你還嵌套使用了 if 語句,問題就會變得更加複雜,由於你在生成和計算一個決策樹時,你的大腦必須像喝醉酒的猴子同樣在決策樹上跳來跳去。這樣會大大下降代碼的可讀性。記住,在編寫代碼時,你應該考慮在你以後要去維護它的會是哪一個傻瓜。也許,就是你本身。git

做爲必需要去維護本身代碼的傻瓜,我最近一直有意識地避免在 JavaScript 中編寫 if 語句。我並不老是可以成功,但我注意到,至少它迫使我從一個徹底不一樣的角度來思考解決問題的方法。它使我成爲一個更好的開發人員,由於它讓我動腦子思考,不然我將會清閒地坐在豆袋上吃花生,而讓 if 語句去完成全部的工做。github

在避免編寫 if 語句的過程當中,我發現我喜歡 JavaScript 中的三元運算符和邏輯操做符組合使用的方式。我如今想建議你的是不太受歡迎的三元運算符,你可使用它與 &&|| 操做符一塊兒編寫一些很是簡潔和具備可讀性的代碼。

不受歡迎的三元運算符

當我剛開始學習編程時,人們常說,「永遠不要使用三元運算符」,它們太複雜了。因此我沒有使用它。一直都沒有。我歷來沒用過三元運算符也從未費心去質疑那些人的說法是否正確。

但如今,我不這麼認爲。

三元運算符只是去用一行代碼來表示的 if 語句而已。絕對的說它們在任何狀況下都太過複雜是不正確的。個人意思是,並非我要特立獨行,但我能夠徹底理解一個簡單的三元運算符。當咱們說要永遠去避免使用它們的時候,咱們是否是有點兒孩子氣了呢?我認爲一個結構良好的三元運算符是能夠賽過一個 if 語句的。

讓咱們舉一個簡單的例子。假設咱們有一個應用程序,咱們想在其中檢查用戶的登陸狀態。若是已登陸,咱們就跳轉到他們的我的主頁。不然,咱們將跳轉到登陸頁面。下面是標準的 if 邏輯語句:

if (isLogggedIn) {
  navigateTo('profile');
}
else {
  navigateTo('unauthorized');
}
複製代碼

用這 6 行代碼來完成工做是很是簡單的。6 行,請記住,你每運行 1 行代碼,必須記住上面代碼的運算結果以及它如何影響下面的代碼。

下面是三元運算符的實現代碼:

isLoggedIn ? navigateTo('profile') : navigateTo('unauthorized');
複製代碼

你的大腦只須要計算這一行,而不是 6 行。你不須要在代碼上下行之間移動,也不須要記住以前的內容。

不過,三元運算符的一個缺點是不能只針對一種狀況進行邏輯判斷。仍是剛剛的例子,若是你想在用戶已登陸時跳轉到他的我的主頁,而若是沒有登陸,則不採起任何操做,下面的代碼將不起做用:

// !! 沒法編譯 !!
logggedIn ? navigateTo('profile')
複製代碼

你不得不在這裏使用 if 語句來完成工做。可是還有沒有其餘方法?

當你只想處理邏輯條件的一個分支但又不想使用 if 語句時,能夠在 JavaScript 中使用這樣一個技巧。你能夠利用 JavaScript 中的 ||(或)和 &&(與)運算符一塊兒工做的方式來實現這一點。

loggedIn && navigateTo('profile');
複製代碼

這是如何實現的?

咱們在這裏實際是在判斷:」這兩個語句都是 true 嗎?」 若是第一項爲 false,JavaScript 引擎就不會再去執行第二項了。由於其中一個已是 false 了,因此咱們知道結果不是 true。咱們利用了這個機制:若是第一項爲 false,JavaScript 就不會去執行第二項。也就是說,「若是第一項爲 true,那麼就去執行第二項」。

若是我想換過來呢?咱們只想在用戶沒有登陸的狀況下導航到用戶主頁,該怎麼辦呢?你能夠直接在 loggedIn 變量前面使用 !,但也有另外一種方法。

loggedIn || navigateTo('profile');
複製代碼

這句代碼的意思是,「這兩個語句會有一個是 true 嗎?」 若是第一項是 false,就必須對第二項進行計算才能肯定。若是第一項爲 true,就永遠不會去執行第二項,由於已經知道其中一項爲 true 了,所以整個語句的結果是 true

那下面這種方式如何呢?

if (!loggedIn) navigateTo('profile');
複製代碼

不,在這種狀況下,不推薦使用。因此,一旦知道可使用 &&|| 運算符來實現 if 語句的功能,就可使用它們來極大地簡化咱們的代碼。

下面是一個更復雜的例子。假設咱們有一個 login 函數,接收一個 user 對象做爲參數。該對象可能爲空,所以咱們須要檢查 local storage,以查看用戶是否在本地保存了會話。若是保存了,而且他是一個管理員用戶,那麼咱們將跳轉到首頁。不然,咱們將導航到另外一個頁面,該頁面提示用戶還未經受權。下面是一個簡單的 if 語句的實現。

function login(user) {
  if (!user) {
    user = getFromLocalStorage('user');
  }
  if (user) {
    if (user.loggedIn && user.isAdmin) {
      navigateTo('dashboard');
    }
    else {
      navigateTo('unauthorized');
    }
  }
  else {
    navigateTo('unauthorized');
  }
}
複製代碼

噢。這也許很複雜,由於咱們對 user 對象作了不少是否爲空的條件判斷。我不但願個人代碼太過複雜,因此讓咱們簡化一下,由於這裏有不少冗餘的代碼,咱們須要封裝一些函數。

function checkUser(user) {
  if (!user) {
    user = getFromLocalStorage('user');
  }
  return user;
}

function checkAdmin(user) {
  if (user.isLoggedIn && user.isAdmin) {
    navigateTo('dashboard');
  }
  else {
    navigateTo('unauthorized');
  }
}

function login(user) {
  if (checkUser(user)) {
    checkAdmin(user);
  }
  else {
    navigateTo('unauthorized');
  }
}
複製代碼

login 函數更簡單了,但實際上代碼更多了,當你考慮到整個代碼而不只僅是 login 函數時,它並不必定是更簡潔的。

我建議放棄使用 if 語句,而使用三元運算符,而且使用邏輯運算符,那麼咱們能夠在兩行代碼中完成全部這些操做。

function login(user) {
  user = user || getFromLocalStorage('user');
  user && (user.loggedIn && user.isAdmin) ? navigateTo('dashboard') : navigateTo('unauthorized')
}
複製代碼

就是這樣。全部煩人的 if 語句代碼塊摺疊後都有兩行。若是第二行代碼看起來有點長,而且影響閱讀,那麼能夠對其進行換行,使它們各自獨處一行。

function login(user) {
  user = user || getFromLocalStorage("user");
  user && (user.loggedIn && user.isAdmin)
    ? navigateTo("dashboard")
    : navigateTo("unauthorized");
}
複製代碼

若是你擔憂別人可能不知道 &&|| 運算符在 JavaScript 中是如何工做的,請添加一些註釋並格式化你的代碼。

function login(user) {
  // 若是 user 爲空,則檢查 local storage
  // 查看是否保存了 user 對象
  user = user || getFromLocalStorage("user");
  
  // 確保 user 不爲空,同時
  // 是登陸狀態而且是管理員。不然,拒絕訪問。
  user && (user.loggedIn && user.isAdmin)
    ? navigateTo("dashboard")
    : navigateTo("unauthorized");
}
複製代碼

其餘

也許,你還可使用一些其餘技巧來處理 JavaScript 條件判斷。

賦值

我最喜歡的技巧之一(在上面使用過)是一個單行代碼來判斷一個變量是否爲空,而後若是爲空就從新賦值。使用 || 運算符來完成。

user = user || getFromLocalStorage('user');
複製代碼

你能夠一直判斷下去:

user = user || getFromLocalStorage('user') || await getFromDatabase('user') || new User();
複製代碼

這也適用於三元運算符:

user = user ? getFromLocalStorage('user') : new User();
複製代碼

組合條件

你能夠爲三元運算符提供多個執行語句。例如,若是咱們想要同時記錄用戶已經登陸的日誌,而後跳轉頁面,就能夠這樣作,而不須要將全部這些操做封裝到另外一個函數中。以下,使用括號括起來,並用逗號隔開。

isLoggedIn ? (log('Logged In'), navigateTo('dashboard')) : navigateTo('unauthorized');
複製代碼

這也適用於 &&|| 操做符:

isLoggedIn && (log('Logged In'), navigateTo('dashboard'));
複製代碼

嵌套三元運算符

你能夠嵌套使用三元運算符。Eric Elliot 在關於三元運算符的文章中經過下面的例子說明了這一點:

const withTernary = ({
  conditionA, conditionB
}) => (
  (!conditionA)
    ? valueC
    : (conditionB)
    ? valueA
    : valueB
);
複製代碼

Eric 在這裏作的最有趣的一點是否認了第一個條件,這樣你就不會把問號和冒號放在一塊兒,否則就會難以閱讀。我要更進一步,給代碼添加一些縮進。同時我還添加了大括號和顯式的返回語句,由於只有括號的話會讓我覺得正在調用一個函數,實際上並無。

const withTernary = ({ conditionA, conditionB }) => {
  return (
    (!conditionA)
      ? valueC  
      : (conditionB)
        ? valueA
        : valueB
  )
}
複製代碼

通常來講,你不該該嵌套使用三元運算符或 if 語句。以上任何一篇 Hacker News 的文章都會讓你羞愧地得出一樣的結論。雖然我不是來羞辱你的,但我只想說,若是你再也不過分使用 if 語句,或許(只是或許)你之後會感謝你本身的。


這就是我對被誤解的三元運算符和邏輯運算符的見解。我認爲它們能夠幫助你編寫簡潔、可讀的代碼,並徹底避免 if 語句。如今,咱們能夠像 Linus Torvalds 同樣用 「good taste」 來結束這一切了。我也能夠早點退休,而後平靜地度過餘生。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索