优雅的链式取值

整理微信上看到的文章,推荐学习
在调数据接口时,都会遇到链式取值的问题,如:

res.data.goods.list[0].price

为了良好的处理报错(Uncaught TypeError: Cannot read property ‘goods’ of undefined),我们一般会判断取值是否为空,即

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
}

如果取得层级更深的话,代码看起来让人头大。所以问题来了:怎么优雅的链式取值?

这里介绍三种方法:
一、通过函数解析字符串
我们可以通过函数解析字符串来解决这个问题,这种实现就是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

二、使用解构赋值
使用ES6的解构赋值,详细介绍请参考本人另一篇博文《变量的解构赋值及用途》

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,我们仍然需要定义默认值, 就像这样

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,方法二也不失为一种好的选择。

猜你喜欢

转载自blog.csdn.net/wy6250000/article/details/83713599