Предположим, у меня есть объект json, в котором я записываю количество посетителей моего сайта, сгруппированных по браузеру / версии.

let data = {
    browsers: {
        chrome: {
           43 : 13, 
           44 : 11
        }, 
        firefox: {
           27: 9
        }
    }
}

Чтобы увеличить конкретный браузер, мне нужно проверить, существует ли несколько ключей, и, если нет, создать их.

let uap = UAParser(request.headers['user-agent']);

if (typeof uap.browser !== 'undefined') {

    if (typeof data.browsers === 'undefined') 
        data.browsers = {}

    if (typeof data.browsers[uap.browser.name] === 'undefined')
        data.browsers[uap.browser.name] = {}

    if (typeof data.browsers[uap.browser.name][uap.browser.version] === 'undefined')
        data.browsers[uap.browser.name][uap.browser.version] = 0

    data.browsers[uap.browser.name][uap.browser.version] += 1;
}

Чем глубже моя структура данных, тем сумасшедшие вещи.

Такое чувство, что должен быть более аккуратный способ сделать это в JavaScript. всегда аккуратнее. Кто-нибудь может просветить меня здесь?

1
roryok 28 Авг 2017 в 14:11

3 ответа

Лучший ответ

Этот короткий код должен сделать свое дело:

if (uap.browser) { // typeof is pretty much redundant for object properties.
    const name = uap.browsers.name; // Variables for readability.
    const version = uap.browser.version;

    // Default value if the property does not exist.
    const browsers = data.browsers = data.browsers || {}; 
    const browser = browsers[name] = browsers[name] || {};
    browser[version] = browser[version] || 0;

    // Finally, increment the value.
    browser[version]++;
}

Обратите внимание, что вы использовали === там, где вы должны были использовать ==== {}).

Давайте объясним эту строку:

const browsers = data.browsers = data.browsers || {};

Последняя часть: data.browsers = data.browsers || {} устанавливает data.browsers в себя, если он существует. Если это еще не сделано, значит, это новый пустой объект.
Затем все это значение присваивается browsers, для удобства доступа.

Теперь более короткий код не должен быть главным приоритетом, но в подобных случаях вы можете сделать код более читабельным.

2
Cerbrus 28 Авг 2017 в 11:41

Вот очень чистое и общее решение, использующее Proxy() У меня есть второе решение, которое является стандартным ECMAscript 5, если оно вам не нужно так чисто или если оно менее зависимо от браузера.

var handler = {
    get: function (target, property) {
        if (property !== "toJSON" && target[property] === undefined) {
            target[property] = new Proxy ({}, handler);
        }
        return target[property];
    }
}
var jsonProxy = new Proxy ({}, handler);

jsonProxy.non.existing.property = 5;
jsonProxy.another.property.that.doesnt.exist = 2;
jsonProxy["you"]["can"]["also"]["use"]["strings"] = 20;

console.log (JSON.stringify (jsonProxy));

Вы можете сделать то же самое с классами, но с более подробным синтаксисом:

var DynamicJSON = function () {};
DynamicJSON.prototype.get = function (property) {
    if (this[property] === undefined) {
        this[property] = new DynamicJSON ();
    }
    return this[property];
};
DynamicJSON.prototype.set = function (property, value) {
    this[property] = value;
};

var jsonClass = new DynamicJSON ();
jsonClass.get("non").get("existing").set("property", 5);
jsonClass.get("you").get("have").get("to").get("use").set("strings", 20);

console.log (JSON.stringify (jsonClass));
0
PilotInPyjamas 28 Авг 2017 в 12:10

Вы можете отказаться от операторов if и сделать это следующим образом:

uap.browser = uap.browser || {}

По сути, он делает то же самое, что если бы только намного короче

0
Tomer 28 Авг 2017 в 11:17