今晚我想來點 nullish coalescing operator

蟲探理查
11分鐘風格

type coercion

一般寫程式的時候像在跟機器講話,而且他們通常邏輯比你好,又脾氣不太好。

機器:「你要不要吃晚餐?」

Richard:「今晚我想來點兇巴巴的起司牛肉可頌」

機器:「伶杯是先問你要不要吃?還沒問你吃什麼?」

幸運的是,在 javascript 這個語言中,機器個性稍微的隨和一點。他看你有回答晚餐的名字,就當你要吃晚餐,所以接著把你的晚餐顯示在螢幕上。

  const dinnerOfRichard = '起司牛肉可頌' //我回答「起司牛肉可頌」
  //機器決定要顯示什麼
  const coercion = (dinnerOfPerson) => {
  //如果dinnerOfPerson沒有東西,也就是null、undefined或空字串,javascript就會把它視為false
    if(dinnerOfPerson){
      console.log('是的你晚上要吃晚餐,而且要吃:',dinnerOfRichard)
    } else {
      console.log('你不吃晚餐')
    }
  }
  coercion(dinnerOfRichard);//螢幕顯示「是的你晚上要吃晚餐,而且要吃:起司牛肉可頌」
  const dinnerOfJolin = ''//Jolin沒有回答
  coercion(dinnerOfJolin);//螢幕顯示「你不吃晚餐」

如果你不回答,或是回答不吃(false 、0 、空字串、null 、undefined) ,機器也都可以接受這些答案,並且依照你計畫的顯示:「你不吃晚餐」,機器心理會想著「既然你沒有說食物的名字,那麼我就當你不吃晚餐好了。」

這就稱為 coalescing。

這也是為什麼有人認為 javascript 的 type coercion太隨便了,如果寫出有 bug 的程式,會不容易把bug 找出來。但是另一方面來想,這是 javascript 的一個重要的特色,可以讓程式碼很簡短,又很符合人類的思考方式。

type coercion 在 javscript 裡面應用的很廣,例如「且」和「或」的邏輯運算上。

「且」和「或」

「且」和「或」不但是機器很常用的邏輯,人類也很常用,例如這段程式碼:

  const isGangGoing = true; //小剛要去爬山
  const isXienGoing = true; //小嫺要去爬山
  //機器決定我是否去爬山
  const  wantHiking = (isGangGoing,isXienGoing)=>{
    if(isGangGoing && isXienGoing) {
      console.log('好,我去爬山')
    } else {
      console.log('我不去爬山')
    }
  }
  wantHiking(isGangGoing,isXienGoing);
  //螢幕顯示「好,我去爬山」

翻譯成人類的語言就是:以 && 為界線,如果左邊的小剛要去爬山 (isGangGoing) ,而且 (&&) ,右邊的小嫺要去爬山 (isXienGoing) 的話,兩個條件同時成立的話,我就去爬山,否則,我就不去爬山。

如果程式碼裡面的 && (且)改成 || (或),這段程式翻譯成人類的語言就會變成:如果左邊的小剛要去爬山,或 (||) ,右邊的小嫺要去爬山,任何一個條件成立,我就去爬山,否則,我就不去爬山。

老鷹
馬拉邦山的老鷹

我覺得很有意思的一個地方是 javascript 的 && 和 || ,用和我們人類一般直覺不一樣的思考方式來得到一樣的結果, && 的部分 JS 的思考方式翻譯成人類語言變成:

如果小剛要去爬山 (isGangGoing 是 true) 的話,就讓小嫺要不要去爬山 (isXienGoing 是 true 或 false),來決定我要不要去爬山。也就是小嫺去爬我就去爬山,否則,我就不去爬山。如果小剛不要去爬山的話 ( isGangGoing 是 true) ,不管小嫺要不要去爬山 (isXienGoing是 true 或 false) ,我都不會去爬山。

|| 的部分就變成:

如果小剛要去爬山的話,那我就一定會去爬山,不管小嫺要不要去爬山。如果小剛不要去爬山的話,才是小嫺要不要去爬山來決定我要不要去爬山,小嫺去爬我就去爬山,否則,我就不去爬山。


對於「且」和「或」,JS語言和人類判斷和思考過程不同,但是結果卻是一樣的。 JS 的判斷過程:

  • 條件1 && 條件2,代表的意思是如果 && 左邊為 true ,就回傳右邊 (也就是條件2) ;左邊為 false 就回傳左邊。
  • 條件1 || 條件2,代表的意思是如果 || 左邊是 true ,就回傳左邊 (條件1) ;左邊是 false ,就回傳右邊 (條件2) 。

JS的判斷結果:

  • 條件1 && 條件2 => 兩個都 true 才是 true ,一個是 false 就是 false 了。
  • 條件1 || 條件2 => 兩個其中有一個 true 就是 true ,兩個都 false 才是 false 。

如果配合一開始提到的 type coercion , && 和 || 的左右兩側的變數不一定要放 true 和 false 這種非黑即白的條件,可以是「起司牛肉可頌」這種答案,意思是Richard否定、空白、沒有Richard這個人 (false, null, undefined, 0, 空字串) 會被當成 false,其他答案會被當成 true。

最精彩的地方是,他判斷完 true 和 false 之後,回傳的不是 true 或 false ,而是裡面是什麼直接回傳什麼,例如直接回傳「起司牛肉可頌」這個答案。之所以用這種方式來判斷,是因為又可以讓程式更變態的精簡了,例如以下的寫法:

  let dinnerOfJolin = `起司牛肉可頌` //Jolin要吃「起司牛肉可頌」
  //機器把點的餐顯示在螢幕上
  const coercion = (dinnerOfPerson) => {
     console.log(dinnerOfPerson && `今晚來點${dinnerOfPerson}`)
  }
  coercion(dinnerOfJolin); //螢幕顯示「今晚來點起司牛肉可頌」,因為Jolin有點餐
  dinnerOfJolin = ``//Jolin什麼都沒說
  coercion(dinnerOfJolin);//螢幕不會出現「今晚來點」,而是什麼都不會出現

翻譯成人類的語言就是,如果 dinnerOfJolin 有點東西的話 (起司牛肉可頌) ,就判斷為 true ,然後回傳 && 右邊,直接顯示「今晚來點起司牛肉可頌」,沒東西的話,就什麼都沒顯示,因為是回傳&&左邊,也就是 dinnerOfJolin 。

反之 || 的使用情況是這樣

  let dinnerOfJolin = `` //Jolin什麼話都沒說
  //機器把Jolin點的餐顯示在螢幕上
  const coercion = (dinnerOfJolin) => {
    console.log(dinnerOfJolin || `Jolin沒點餐喔`)
  }
  coercion(dinnerOfJolin);//螢幕出現「Jolin沒點餐喔」
  dinnerOfJolin = `今晚來點起司牛肉可頌`//Jolin說「今晚來點起司牛肉可頌」
  coercion(dinnerOfJolin);//螢幕出現「今晚來點起司牛肉可頌」

翻譯成人類的語言就是,如果 dinnerOfJolin 裡面沒有東西的話,就判斷為 false ,然後直接 顯示||右邊的「 Jolin 沒點餐喔」,裡面有東西的話 (起司牛肉可頌) ,就直接顯示 || 左邊的「今晚來點」和「起司牛肉可頌」。

nullish coalescing operator

讀到這裡,相信我們已經非常了解 && ||是什麼和怎麼用,那麼 ES2020 的 ?? (nullish coalescing operator) 就不難了,簡單來說,他只是用來在特殊場合取代 || 的。

例如: 機器問:「晚餐你要付多少錢?」

我原本希望電腦螢幕在我厚臉皮回答 0 的時候顯示「0」,我沒回答的時候顯示「你還沒回答」。

但是 JS 原本的 || 的程式碼無法達成我想要的效果,例如以下程式碼:

  let payOfRichard=0 //我回答0
  //機器顯示我要付多少錢
  const coercion = (payOfRichard) => {
    console.log(payOfRichard || `你還沒回答`)
  }
  coercion(payOfRichard)//螢幕顯示「你還沒回答」

上面會顯示「你還沒回答」,這不是我們想要的,我們希望螢幕顯示「0」。這時後,我們把 || 改成 ?? 就心想事成了,如下面的程式看得出來,我什麼都還沒說,顯示的就是「你還沒回答」。

  let payOfRichard; //我什麼都還沒說
  //機器顯示我要付多少錢
  const nco = (payOfRichard) => {
    console.log(payOfRichard ?? `你還沒回答`)
  }
  nco(payOfRichard) //顯示「你還沒回答」
  payOfRichard=0 //我回答0
  nco(payOfRichard) //顯示「0」
  payOfRichard=100
  nco(payOfRichard) //顯示「100」

有了 nullish coalescing operator,我們碼農又可以用更簡短的程式,命令機器對付更複雜的情況了。這裡簡單提供一下可能應用的三種使用情況:

  1. 想要沒有值的時候什麼都不顯示,有值的時候顯示值,就用 && 。
  2. 想要沒有值或值是 0 的時候,顯示預設的訊息,有值的時候顯示值,就用 || 。
  3. 想要沒有值的時候顯示預設訊息,有值的時候,不管值是 0 或不是 0 ,都要顯示值,就用 ?? 。

講到這裡剛好想到機器這樣的思考方式,其實在人類生活中也可能遇到,例如以前我在洽談專利案件時,某些公司老闆要辦專利,最好談案件時邀請他夫人一起了解,因為通常老闆找你來談案件的時候是已經決定要辦了,但是管錢的夫人還沒同意,我們剛學到的機器思考老闆 && 老闆娘的方式就用上了,成交與否其實和老闆娘的關係比較大,讓董娘也了解什麼是專利,為什麼要申請專利,成交的機率似乎較大。

五月 11, 2021 更新