Как элегантно связать значения

внешний интерфейс GitHub Babel Underscore.js
Как элегантно связать значения

В разработке связанные значения — это очень обычные операции, такие как:

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
}

Я не могу представить, что произошло бы, если бы уровень данных был немного глубже.Эта реализация действительно неэлегантна, так что, если цепочка значений реализована элегантно?

1. необязательная цепочка

Это новый синтаксис ecma для этапа 2, и уже есть плагин для 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метод

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

3. Используйте деструктурирующее присваивание

Эта идея из githubYou-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

Конечно, чтобы гарантировать, что в это время не будет сообщено о неперехваченной ошибке Typeerror, нам все еще нужно определить значение по умолчанию, например, кажется, что если lint не добавлен, читабельность вызывает беспокойство.

const {a: {c: {d: result2} = {}}} = c

4. Используйте прокси

Об этом упоминают коллеги по группе.Простая реализация выглядит следующим образом:

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

Это почти так называемая элегантность в моем сердце.

Подводя итог, в реальной работе четвертый метод является наиболее элегантным и читабельным, но с учетом браузера, второй метод может использоваться чаще, Конечно, если уровень значения, который вы хотите взять, не слишком deep, Коллеги по вашей группе должны строго линтовать, и способ 3 тоже хороший выбор.

Последний рекламный пост.Недавно открылся новый паблик для обмена технологиями.Не знаю сколько он продлится.Приглашаю всех обратить внимание👇