21

从嵌套结构中取值时如何编写兜底逻辑

 3 years ago
source link: http://www.cnblogs.com/dashnowords/p/13905413.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

从嵌套结构中取值时如何编写兜底逻辑

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

博客园地址: 《大史住在大前端》原创博文目录

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

华为云社区地址: 【你要的前端打怪升级指南】

字节跳动幸福里 大前端团队 邀请各路高手前来玩耍,团队和谐有爱,技术硬核,字节范儿正,覆盖前端各个方向技术栈,总有位置适合你,Base北京,社招实习都有HC,不要犹豫,内推简历请直接瞄准[email protected]~

目录

  • 从嵌套结构中取值时如何编写兜底逻辑
    • 方案1——Lodash.get方法
    • 方案2——使用babel可选链插件
    • 方案3——利用函数式编程实现get方法
    • babel可选链的编译结果:

示例代码:

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进行了容错处理。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK