1. 程式人生 > >從巢狀結構中取值時如何編寫兜底邏輯

從巢狀結構中取值時如何編寫兜底邏輯

# 從巢狀結構中取值時如何編寫兜底邏輯 > github總基地:[http://www.github.com/dashnowords/blogs](https://github.com/dashnowords/blogs/) > > 部落格園地址:[《大史住在大前端》原創博文目錄](https://www.cnblogs.com/dashnowords/p/10127926.html) > > 掘金地址:https://juejin.im/user/2946346892662136 > > 華為雲社群地址:[【你要的前端打怪升級指南】](https://bbs.huaweicloud.com/blogs/8ae7e6420a4611e9bd5a7ca23e93a891) > > 位元組跳動幸福裡**大前端團隊**邀請各路高手前來玩耍,團隊和諧有愛,技術硬核,位元組範兒正,覆蓋前端各個方向技術棧,總有位置適合你,Base北京,社招實習都有HC,不要猶豫,內推簡歷請直接瞄準[email protected]~ [TOC] ### 示例程式碼: ```js let { a = [] } = b || {}; a.map(item => { item.headerTpl = buildHeader(item); }); ``` ### 問題分析: 對a解構時賦予的預設值(空陣列),僅當b.a的值為undefined時才會生效,如果b.a的值為null,預設值就無法生效,使得第二行呼叫map方法的程式碼直接報錯,所以第一行程式碼兜底並沒有做好。 ### 方案1——Lodash.get方法 > 結論:**數值挖取和後續處理統一使用lodash提供的方法**,例如_.map()等基本可以避免在業務層充斥過多校驗和防禦程式碼,lodash的API語義化也相對清晰,容易理解開發者意圖。但如果和ES6原生方法配合的話,還需要繼續做容錯處理以免被null坑。 • 路徑中有null或undefined時,即使有後續取值路徑,也不會報錯,而是返回預設值 • 如果取到的值為null,則返回null(不會觸發預設值),所以對於期望型別為陣列型別的,下一步如果想呼叫原生陣列方法,仍然需要進行型別容錯,如果配合lodash提供的其他方法則不用容錯。 API和原始碼地址:https://lodash.com/docs/4.17.15#get ```js const get = require('lodash/get'); const response = { "data": { "history": [{ "date": "2020-09-11", "eat": 0, "sleep": 0, "total": { "student1": { "eat": 9, "sleep": 0 } } },{ "date": "2020-08-21", "eat": 0, "sleep": 53, "total": { "student1": { "eat": 0, "sleep": 13 }, "student1": { "eat": 0, "sleep": 53 } } }], "test":{ "test_undefined": undefined, "test_null": null } }, "message": "success", "status": 0 } //常規取值 let result1=get(response,'data.history[1].total.student1','defaultValue'); let result2=get(response,'data.history[3].total.student1','defaultValue'); let result3 = get(response, 'data.test.test_undefined','defaultValue'); let result4 = get(response, 'data.test.test_null','defaultValue'); let result5 = get(response, 'data.test.test_undefined.lark','defaultValue'); let result6 = get(response, 'data.test.test_null.lark','defaultValue'); console.log(result1); // {eat:0, sleep:13} console.log(result2); // defaultValue console.log(result3); //defaultValue console.log(result4); //null console.log(result5); //defaultValue console.log(result6); //defaultValue ``` ### 方案2——使用babel可選鏈外掛 > 結論:實現原理和語法都更精簡,可以更好地配合ES6原生方法。 > > • 路徑中有null或undefined時,即使有後續取值路徑,也不會報錯,而是返回預設值 > > • 最終結果為undefined或null時都返回預設值(和lodash.get的區別) > > • [MDN中關於可選鏈的描述](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/可選鏈) 首先配置babel外掛: ```js { "plugins": [ "@babel/plugin-proposal-nullish-coalescing-operator", "@babel/plugin-proposal-optional-chaining" ] } ``` 在程式碼中使用可選鏈: ```js const response = { "data": { "history": [{ "date": "2020-09-11", "eat": 0, "sleep": 0, "total": { "student1": { "eat": 9, "sleep": 0 } } },{ "date": "2020-08-21", "eat": 0, "sleep": 53, "total": { "student1": { "eat": 0, "sleep": 13 }, "student2": { "eat": 0, "sleep": 53 } } }], "test":{ "test_undefined": undefined, "test_null": null } }, "message": "success", "status": 0 } let result1 = response.data?.history[1]?.total?.student1 ?? 'defaultValue'; let result2 = response.data?.history[3]?.total?.student1 ?? 'defaultValue'; let result3 = response.data?.test?.test_undefined ?? 'defaultValue'; let result4 = response.data?.test?.test_null ?? 'defaultValue'; let result5 = response.data?.test?.test_undefined?.lark ?? 'defaultValue'; let result6 = response.data?.test?.test_null?.lark ?? 'defaultValue'; console.log(result1); // {eat:0, sleep:13} console.log(result2); // defaultValue console.log(result3); // defaultValue console.log(result4); // defaultValue console.log(result5); // defaultValue console.log(result6); // defaultValue ``` ### 方案3——利用函數語言程式設計實現get方法 > 原文可見:[如何優雅安全地在深層資料結構中取值](https://zhuanlan.zhihu.com/p/27748589) ```js /** * * @param {*} p ['a','b'....] 屬性路徑 * @param {*} o 待取值物件 * @param {*} d 預設值 defaultValue */ const get = (p, o, d) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : d, o); ``` ### babel可選鏈的編譯結果: 原始碼: ```js const a = { b: { c: { d: null } } }; let r = a.b?.c?.d ?? "defaultValue"; ``` 編譯後: ```js var _a$b$c$d, _a$b, _a$b$c; const a = { b: { c: { d: null } } }; let r = (_a$b$c$d = (_a$b = a.b) === null || _a$b === void 0 ? void 0 : (_a$b$c = _a$b.c) === null || _a$b$c === void 0 ? void 0 : _a$b$c.d) !== null && _a$b$c$d !== void 0 ? _a$b$c$d : "defaultValue"; ``` 基本邏輯可以按括號從內往外看,並不複雜,就是每次取屬性都對undefined和null進行了容錯處