從嵌套結構中取值時如何編寫兜底邏輯

從嵌套結構中取值時如何編寫兜底邏輯

github總基地:http://www.github.com/dashnowords/blogs

博客園地址:《大史住在大前端》原創博文目錄

掘金地址:https://juejin.im/user/2946346892662136

華爲雲社區地址:【你要的前端打怪升級指南】

字節跳動幸福裏大前端團隊邀請各路高手前來玩耍,團隊和諧有愛,技術硬核,字節範兒正,覆蓋前端各個方向技術棧,總有位置適合你,Base北京,社招實習都有HC,不要猶豫,內推簡歷請直接瞄準[email protected]~

示例代碼:

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

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中關於可選鏈的描述

首先配置babel插件:

{
 "plugins": [
    "@babel/plugin-proposal-nullish-coalescing-operator",
    "@babel/plugin-proposal-optional-chaining"
  ]
}

在代碼中使用可選鏈:

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方法

原文可見:如何優雅安全地在深層數據結構中取值

/**
 * 
 * @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可選鏈的編譯結果:

源代碼:

const a = {
  b: {
    c: {
      d: null
    }
  }
};
let r = a.b?.c?.d ?? "defaultValue";

編譯後:

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進行了容錯處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章