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

举报
大史不说话 发表于 2020/10/31 10:56:25 2020/10/31
【摘要】 从嵌套结构中取值时如何编写兜底逻辑 示例代码:问题分析:方案1——Lodash.get方法方案2——使用babel可选链插件方案3——利用函数式编程实现get方法babel可选链的编译结果:示例代码:let { a = [] } = b || {}; a.map(item => { item.headerTpl = buildHeader(item);});问题分析:对a解构时赋予的...

从嵌套结构中取值时如何编写兜底逻辑 示例代码:问题分析:方案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进行了容错处理。



【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。