爆炸的起因
在实务上,我们常常会碰到「把得到的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为name
的value
是不是等于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这个恶梦,欢迎大家分享,也欢迎指正文章,谢谢~