做者:@灰风GreyWind前端
https://juejin.im/post/5ba08483e51d450e99430a7fgit
开发中,链式取值是很是正常的操做,如:github
res.data.goods.list[0].price
可是对于这种操做报出相似于Uncaught TypeError: Cannot read property 'goods' of undefined
这种错误也是再正常不过了,若是说是res数据是本身定义,那么可控性会大一些,可是若是这些数据来自于不一样端(如先后端),那么这种数据对于咱们来讲咱们都是不可控的,所以为了保证程序可以正常运行下去,咱们须要对此校验:swift
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
}
不敢想象,若是数据的层级再深一点会怎样,这种实现实在是很是不优雅,那么若是优雅地来实现链式取值呢?浏览器
1、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
2、经过函数解析字符串
咱们能够经过函数解析字符串来解决这个问题,这种实现就是lodash的 _.get
方法babel
var object = { a: [{ b: { c: 3 } }] };
var result = _.get(object, 'a[0].b.c', 1);
console.log(result);
// output: 3
实现起来也很是简单,只是简单的字符串解析而已:app
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
3、使用解构赋值
这个思路是来自github上 You-Dont-Need-Lodash-Underscore 这个仓库,看到这个的时候真的佩服函数
const c = {a:{b: [1,2,3,4]}}
const { a: result } = c;
// result : {b: [1,2,3,4]}const {a: { c: result = 12 }} = c
// result: 12
固然,这个时候为了保证不报uncaught Typeerror,咱们仍然须要定义默认值, 就像这样, 貌似若是不加lint可读性堪忧
const {a: {c: {d: result2} = {}}} = c
4、使用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,方法三也不失为一种好的选择。
本文分享自微信公众号 - 前端自习课(FE-study)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。