Как поймать падающий «неопределенный» нож в объектных литералах?

#javascript

#javascript

Вопрос:

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

 .object    
     .result2[0] <--undefined could show its ugly face here, or anywhere above or below!
              .thumbnails[0]
                            .type
                                 .name
                                      .['open']
  

Это не работает:

 if ( typeof object.result2[0].thumbnails[0].....  type == 'undefined'){     
    console.log("err'd out")
    handleError();
}
  

Итак, я предполагаю, что я ищу решение, которое следует: если что-либо внутри object не определено, сделайте что-нибудь, или я лаю не на то дерево?

Комментарии:

1. Лично я обычно просто использую try / catch или несколько if условий для этого.

2. Конечно, что в этом плохого? Try / catch — это легитимный код. Если вы подозреваете, что может быть вызвана другая ошибка, вы всегда можете проверить содержимое ошибки и повторно вызвать или обработать, если это не исключение с нулевой ссылкой. В try / catch нет ничего изначально злого, только некоторые потенциальные накладные расходы на создание информации об исключении.

Ответ №1:

Если вы часто занимаетесь подобными вещами, то небольшая вспомогательная функция полезна:

 function dig_out(o, path, def) {
    var parts = path.split('.');
    for(var i = 0; i < parts.length;   i) { 
        if(typeof o == 'undefined') 
            return def; 
        o = o[parts[i]];  
    }
    return o;
}
obj = { a: [1, [2, { b: 10 } ]]};
var x = dig_out(ob, 'a.1.1.b'); // x is now 10
  

Хитрость в том, чтобы понять, что это:

 object.results2[0].thumbnails[0].type.name["open"]
  

также может быть записан как:

 object['results2'][0]['thumbnails'][0]['type']['name']['open']
  

И это можно легко представить в виде строки:

 'results2.0.thumbnails.0.type.name.open'
  

это легко понять и проанализировать.

Вы также могли бы представить путь в виде массива (как это делает CD Sanchez), но тогда вам пришлось бы что-то делать со значением по умолчанию, def .

Вы также могли бы разрешить path аргументу быть массивом:

 function dig_out(o, path, def) {
    var parts = path instanceof Array ? path : path.split('.');
    for(var i = 0; i < parts.length;   i) {
        if(typeof o == 'undefined')
            return def;
        o = o[parts[i]];
    }
    return o;
}
obj = { a: [1, [2, { b: 10 } ]]};
var x = dig_out(obj, 'a.1.1.b');        // x is now 10
var y = dig_out(obj, ['a', 1, 1, 'b']); // y is now 10
  

Тогда у вас была бы некоторая гибкость в отношении того, с каким форматом аргументов проще всего работать, и это даже не стоило бы так дорого. Спасибо небольшой дискуссии с CD Sanchez за эту идею.

Комментарии:

1. Это потрясающая информация, не специально для этого вопроса, а для моих общих знаний JavaScript. 1.

2. Да, мой был бы более универсальным, если бы его изменили для возврата объекта. Однако в вашем случае я должен прокомментировать, что передача пути в виде строки может быть не лучшим способом сделать это, потому что вы можете в конечном итоге получить уродливые конкатентации строк (или, в лучшем случае, объединение в массив значений), если вы делаете это, например, внутри цикла. например dig_out(object, 'results2.' i '.thumbnails' ...)

3. @CD: Какой подход вы выбрали, будет зависеть от конкретных обстоятельств. Вы также могли бы проверить, был ли второй аргумент строкой или массивом; если это была строка, то разделите ее, если это был массив, то просто используйте его как есть. У вас не было бы вариативного поведения, но с массивом, вероятно, было бы легче работать в куче вложенных циклов.

4. Хорошо подходит для моей ситуации. Спасибо за помощь!

5. @CD: Я добавил обновление с гибкой обработкой аргументов, я думаю, что это дает нам лучшее из обоих миров.

Ответ №2:

Обычно вы используете

 if (object amp;amp; object.result2[0] amp;amp; object.results2[0].thumbnails[0] amp;amp; object.results2[0].thumbnails[0].type amp;amp; object.results2[0].thumbnails[0].type.name) {
    object.results2[0].thumbnails[0].type.name["open"] 
}
  

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

Комментарии:

1. @cube да, ты знаешь. Это или напишите вспомогательную функцию, как у других ребят.

Ответ №3:

Простой блок try-catch сделает свое дело, без необходимости тестировать каждый уровень.

Комментарии:

1. @cube — Есть ли в вашей компании политика против try / catch в производственном коде? Безусловно, существует потенциал для злоупотреблений, но, кроме довольно противоречивой дискуссии Джоэла Спольски на эту тему и некоторых соображений производительности , которые могут быть / не быть актуальными, я не знаю о каком-либо широком консенсусе о том, что try / catch считается вредным.

2. try catch плохо влияет на производительность JavaScript. этого действительно следует избегать, особенно для чего-то такого простого, как это.

Ответ №4:

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

 function pathExists() { // untested, but the basis is sound
    var obj = arguments[0], path = Array.prototype.slice.call(arguments, 1), cursor = obj;
    for (var i = 0; i < path.length;   i) {
        if (typeof cursor[path[i]] == "undefined") return false;
        cursor = cursor[path[i]];
    }
    return cursor;
}

if (!pathExists(object, "result2", 0, "type", "name", "open")) 
   console.log("bork");