zoukankan      html  css  js  c++  java
  • 从嵌套结构中取值时如何编写兜底逻辑

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

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

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

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

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

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

    示例代码:

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

  • 相关阅读:
    [core java学习笔记][第五章继承]
    [core java学习笔记][第四章对象与类]
    【转载】Maven中的BOM概念
    【转载】关于docker某些有用的文章
    【转载】linux Jumpserver跳板机堡垒机部署安装使用教程
    四:(之五)Dockerfile语法梳理和实践
    四:FAQ附录(容器交互,镜像交互,镜像导出)
    四:(之四)基于已有镜像构建自己的Docker镜像
    四:(之三)制作镜像和一些docker命令
    四:(之一和之二) docker架构和底层技术分析(C/S架构)
  • 原文地址:https://www.cnblogs.com/dashnowords/p/13905413.html
Copyright © 2011-2022 走看看