Тестирование вложенных объектов как undefined в Javascript

Возможный дубликат:
javascript test для существования ключа вложенных объектов

Я пытаюсь создать сообщение об ошибке для formset путем тестирования, если определенный объект не является undefined, а если он не undefined, я в конечном итоге заполняю его этим сообщением об ошибке. Основная проблема заключается в том, что я должен проверять, есть ли каждый вложенный объект undefined, что приводит к некоторому довольно уродливому коду. Вот пример:

errorsForField: function(fieldName, formsetName, formNumber) {
            if (typeof this.model.errors != 'undefined'){

                var fieldError = document.createElement('span');
                $(fieldError).addClass('field-error');

                // THE FOLLOWING LINE THROWS ERROR.
                if (formsetName && _.isUndefined(this.model.errors[formsetName][fieldName]) != true) {
                    $(fieldError).text(this.model.errors[formsetname][fieldName]);
                } else if (typeof this.model.errors[fieldName] != "undefined"){
                    $(fieldError).text(this.model.errors[fieldName]);
                }

                this.errors[fieldName] = fieldError.outerHTML;
                return fieldError.outerHTML; 
            }
            return false; 
        },

Я получаю сообщение об ошибке, что я не могу определить [fieldName] для undefined object this.model.errors[formsetName]. Другими словами, я должен сначала определить, является ли this.model.errors[formsetName] пустым, а затем проверить, является ли [fieldName] undefined.

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

Ответ 1

Вы можете создать библиотечную функцию, которая принимает имена свойств в качестве параметров и возвращает окончательное значение, если оно существует, или null:

function TryGetPropertyValue(o, propertyName1 /*, ... propertyNameN */) {
    var names = [].slice.call(arguments, 1);
    while (o && names.length) {
        o = o[names.shift()];
    }
    return names.length ? null : o;
}

Назовите его так:

var err = TryGetPropertyValue(this.model.errors, formsetName, fieldName) ||
          TryGetPropertyValue(this.model.errors, fieldName);
if (err != null) {
    $(fieldError).text(err);
}

Если вы хотите, чтобы он возвращал undefined вместо null, если поле не найдено, вы можете слегка изменить функцию:

function TryGetPropertyValue(o, propertyName1 /*, ... propertyNameN */) {
    var names = [].slice.call(arguments, 1);
    while (o && names.length) {
        o = o[names.shift()];
    }
    if (names.length == 0) {
        return o;
    }
}

http://jsfiddle.net/HbggQ/

Ответ 2

Как сказал Павел, это неотъемлемое ограничение Javascript. Даже Coffeescript (который является всего лишь слоем синтаксического сахара поверх JS) на самом деле не решает проблему; он просто скрывает обходной путь под ним синтаксический сахар (который, по общему признанию, очень удобен)

Если вы хотите придерживаться Javascript, у вас в основном есть два варианта: использовать тернарные операторы или использовать логические операторы. Здесь примеры каждого, которые проверяют A.B.C.D(где A, B, C или D могут отсутствовать):

// Returns A.B.C.D, if it exists; otherwise returns false (via ternary)
return !A ? false :
            !A.B ? false :
                   !A.B.C ? false :
                            A.B.C.D ? A.B.C.D : false;

// Returns A.B.C.D, if it exists; otherwise returns false (via booleans)
return A && A.B && A.B.C && A.B.C.D;

Очевидно, что последнее намного короче. Оба решения полагаются на "правду" Javascript (то есть, что значения 0, "", null и undefined считаются ложными). Это должно быть хорошо для вашего случая, так как ни одно из этих значений не будет иметь свойство errors. Однако, если вам нужно различать (скажем) 0 и undefined, вы можете использовать тройной стиль и заменить !A на typeof(A) == 'undefined'.