美文网首页
如何优雅地链式取值

如何优雅地链式取值

作者: 忽如寄 | 来源:发表于2018-09-16 23:16 被阅读135次

    开发中,链式取值是非常正常的操作,如:

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

    相关文章

      网友评论

          本文标题:如何优雅地链式取值

          本文链接:https://www.haomeiwen.com/subject/ccrfnftx.html