如何优雅地链式取值
栏目: JavaScript · 发布时间: 6年前
内容简介:开发中,链式取值是非常正常的操作,如:但是对于这种操作报出类似于如果再精细一点,对于所有都进行校验的话,就会像这样:
开发中,链式取值是非常正常的操作,如:
res.data.goods.list[0].price 复制代码
但是对于这种操作报出类似于 Uncaught TypeError: Cannot read property 'goods' of undefined
这种错误也是再正常不过了,如果说是res数据是自己定义,那么可控性会大一些,但是如果这些数据来自于不同端(如前后端),那么这种数据对于我们来说我们都是不可控的,因此为了保证程序能够正常运行下去,我们需要对此校验:
if (res.data.goods.list[0] && res.data.goods.list[0].price) { // your code } 复制代码
如果再精细一点,对于所有都进行校验的话,就会像这样:
if (res && res.data && res.data.goods && res.data.goods.list && res.data.goods.list[0] && res.data.goods.list[0].price){ // your code } 复制代码
不敢想象,如果数据的层级再深一点会怎样,这种实现实在是非常不优雅,那么如果优雅地来实现链式取值呢?
一、optional chaining
这是一个出于stage 2的ecma新语法,目前已经有了babel的插件 babel-plugin-transform-optional-chaining ,这种语法在swift中有,可以看下官方给的实例
a?.b // undefined if `a` is null/undefined, `a.b` otherwise. a == null ? undefined : a.b a?.[x] // undefined if `a` is null/undefined, `a[x]` otherwise. a == null ? undefined : a[x] a?.b() // undefined if `a` is null/undefined a == null ? undefined : a.b() // throws a TypeError if `a.b` is not a function // otherwise, evaluates to `a.b()` a?.() // undefined if `a` is null/undefined a == null ? undefined : a() // throws a TypeError if `a` is neither null/undefined, nor a function // invokes the function `a` otherwise 复制代码
二、通过函数解析字符串
我们可以通过函数解析字符串来解决这个问题,这种实现就是lodash的 _.get
方法
var object = { a: [{ b: { c: 3 } }] }; var result = _.get(object, 'a[0].b.c', 1); console.log(result); // output: 3 复制代码
实现起来也非常简单,只是简单的字符串解析而已:
function get (obj, props, def) { if((obj == null) || obj == null || typeof props !== 'string') return def; const temp = props.split('.'); const fieldArr = [].concat(temp); temp.forEach((e, i) => { if(/^(\w+)\[(\w+)\]$/.test(e)) { const matchs = e.match(/^(\w+)\[(\w+)\]$/); const field1 = matchs[1]; const field2 = matchs[2]; const index = fieldArr.indexOf(e); fieldArr.splice(index, 1, field1, field2); } }) return fieldArr.reduce((pre, cur) => { const target = pre[cur] || def; if(target instanceof Array) { return [].concat(target); } if(target instanceof Object) { return Object.assign({}, target) } return target; }, obj) } 复制代码
var c = {a: {b : [1,2,3] }} get(c ,'a.b') // [1,2,3] get(c, 'a.b[1]') // 2 get(c, 'a.d', 12) // 12 复制代码
三、使用解构赋值
这个思路是来自github上 You-Dont-Need-Lodash-Underscore 这个仓库,看到这个的时候真的佩服
const c = {a:{b: [1,2,3,4]}} const { a: result } = c; // result : {b: [1,2,3,4]} cosnt {a: { c: result = 12 }} = c // result: 12 复制代码
当然,这个时候为了保证不报uncaught Typeerror,我们仍然需要定义默认值, 就像这样, 貌似如果不加lint可读性堪忧
const {a: {c: {d: result2} = {}}} = c 复制代码
四、使用Proxy
这个是组内同事提到的,一个简单实现如下:
function pointer(obj, path = []) { return new Proxy({}, { get (target, property) { return pointer(obj, path.concat(property)) }, apply (target, self, args) { let val = obj; let parent; for(let i = 0; i < path.length; i++) { if(val === null || val === undefined) break; parent = val; val = val[path[i]] } if(val === null || val === undefined) { val = args[0] } return val; } }) } 复制代码
我们可以这样使用:
let c = {a: {b: [1, ,2 ,3]}} pointer(c).a(); // {b: [1,2,3]} pointer(c).a.b(); // [1,2,3] pointer(d).a.b.d('default value'); // default value 复制代码
这差不多就是心中所谓的优雅了。
综上,在实际工作中,使用方法四会是最优雅,可读性也非常强,但考虑到浏览器的话,可能方法二会更加常用,当然,如果你所要取的值层级不是太深,你组内的同事要严格的lint,方法三也不失为一种好的选择。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- KotlinTips:getValueSafely 安全取值
- 如何优雅的链式取值之 MayBe 函子
- 前嗅ForeSpider教程:字段的取值与清洗
- jQuery EasyUI DataGrid Checkbox 数据设定与取值
- javascript xml xsl取值及数据修改第1/2页
- 避免取值时出现Cannot read property 'xx' of undefined
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。