iT邦幫忙

4

Week8 - 不想再看到undefined的TypeError嗎,你可以試看看JS的Optional chaining operator [Server的終局之戰系列]

爆炸的起因

在實務上,我們常常會碰到「把得到的response做分析,並產生對應行為的」情境,如下展示為從DB拿回各個人名的資料:

const response = {
  rows: [
    {
      name: 'John'
    }
  ]
}
if (response.rows[0].name === 'John') console.log('Get John')

我們可以發現,response裡面的rows其實是「不太確定」的,如果John在DB被刪除了,就會得到以下response:

const response = {
  rows: []
}
if (response.rows[0].name === 'John') console.log('Get John')

這樣會導致程式碼直接錯誤:

"TypeError: Cannot read property 'name' of undefined

因為在解析response.rows[0]得到的已經是undefined,這時我們再從undefined裡面取出key為name的value時,就會直接爆炸\( `.∀´)/。

該如何避免爆炸

我們可以將程式碼改成如下:

const response = {
  rows: []
}
if (response.rows[0] && response.rows[0].name === 'John') console.log('Get John')

這樣的做法是先檢查response.rows[0]存不存在,如果存在再繼續檢查內部key為namevalue是不是等於John,這樣的方法的確解決了我們的問題。

但如果John的資料越來越多,形成了巢狀object,而我們的情境變為「解析John使用的飛機交通工具是不是Brt707時」,就會變成這樣:

const response = {
  rows: [
    {
      "name": "John",
      "age": 28,
      "vehicles": {
         "car": "Suzuki",
         "bike": "Ubike",
         "airlines":{
            "UNI AIR" : "Air123",
            "Mandarin" : "Brt707"
         }
      }
    }
  ]
}
if (
  response.rows[0] &&
  response.rows[0].vehicles &&
  response.rows[0].vehicles.airlines &&
  response.rows[0].vehicles.airlines.Mandarin === 'Brt707'
) console.log('Get Brt707')

是不是讓人想起了一首名叫洋蔥的歌


解決此問題的救星 - Optional chaining operator

為了解決此問題,在TC39(就是制定許多新語法的協會)有了此項新提案,目前已經進入stage4(如果不太清楚stage可以看此篇文章),在Chrome裡面已經可以使用,這邊直接舉例講解:

const response = {
  rows: [
    {
      "name": "John",
      "age": 28,
      "vehicles": {
         "car": "Suzuki",
         "bike": "Ubike",
         "airlines":{
            "UNI AIR" : "Air123",
            "Mandarin" : "Brt707"
         }
      }
    }
  ]
}
if (response.rows?.[0]?.vehicles?.airlines?.Mandarin === 'Brt707') console.log('Get Brt707')

以rows?來說,會有以下兩種情形

  • 如果rows存在就繼續往下查找[0],
  • 如果rows不存在就直接回傳undefine,並且不會再繼續往下查找[0]

這真的很方便,在MDN有更多的使用情境,但都是與此情境相似,大家可以參考看看

這真的好酷,那我試看看在Node.js,shit...爆炸了

剛剛有提到MDN,我們就來看看MDN下方所提供的support表:

是的,Node.js目前並不支援此用法,但沒關係,我們有Babel.js plugin的幫忙!可以提前使用它,

這個Babel.js就是我們可以使用新版的一些特性,而Babel.js會幫我們把code轉成舊版的code,讓Node.js可以讀懂他

我不太想在後端編譯...有沒有其他方法

有,我們可以用lodash的get,如果找不到值會回傳undefine

const _ = require('lodash')

const response = {
  rows: [
    {
      "name": "John",
      "age": 28,
      "vehicles": {
         "car": "Suzuki",
         "bike": "Ubike",
         "airlines":{
            "UNI AIR" : "Air123",
            "Mandarin" : "Brt707"
         }
      }
    }
  ]
}
if (_.get(response, 'rows.[0].vehicles.airlines.Mandarin') === 'Brt707') console.log('Get Brt707')

jsonpath的query,找不到值會回傳空array。此種方法是json的一種路徑方法,在許多語言都有支持,例如java也擁有jsonpath,

const jp = require('jsonpath');

const response = {
  rows: [
    {
      "name": "John",
      "age": 28,
      "vehicles": {
        "car": "Suzuki",
        "bike": "Ubike",
        "airlines":{
          "UNI AIR" : "Air123",
          "Mandarin" : "Brt707"
        }
      }
    }
  ]
}

if (jp.query(response, '$.rows[0].vehicles.airlines.Mandarin')[0] === 'Brt707') console.log('Get Brt707')

結論

最後我是選擇lodash的get來找尋這些value,主要是因為後端專案要透過Bable.js編譯雖然不需要太多時間,但眾多專案一起來,我的時間就噴了(`Д´*)。

但我非常期待Optional chaining operatory在Node.js原生支持的一天,此方法使用起來非常的順手與漂亮。

你是喜歡哪種方法,或者還有什麼方法可以解決undefine這個惡夢,歡迎大家分享,也歡迎指正文章,謝謝~


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
Robin
iT邦新手 2 級 ‧ 2020-04-06 10:07:05

看到洋蔥那邊不爭氣的笑了xDDD
感謝大大分享

剝開我的心~~

0
Gary
iT邦新手 3 級 ‧ 2020-04-10 22:59:19

哦哦哦哦~
剛好最近碰到類似的問題
還用了一大堆if去防止噴錯
原來還有這種寫法
/images/emoticon/emoticon42.gif

能幫到忙非常開心,哈哈,
不過要注意這方法目前最支援的是前端,
後端需要透過babel之類,就如文章所說~

我要留言

立即登入留言