Доступ к свойствам вложенных объектов через «.» или «[]» опасен тем, что если в цепочке не будет указанного объекта, то будет выброшено исключение.
var obj = {a: {b: {c: 42} } };
obj.a.x.y;
TypeError: Cannot read property ‘y’ of undefined
От таких ситуаций можно обезопаситься, если воспользоваться простой функцией.
function getProperty(obj, prop) {
return prop.split('.')
.reduce(function (m, i) {
return m && typeof m == 'object' ? m[i] : null;
}, obj);
}
var obj = {a: {b: {c: 42} } };
getProperty(obj, 'a.x.y');
null
Коментарии к заметке
Только вот
getProperty(obj, 'a.x.y');
вернёт неundefined
, аnull
.try { obj.a.x.y; } catch (e) {}
Работает значительно быстрее и понятнееМиша, спасибо, поправил заметку.
Антон, увы, перехват ошибок во всех языках программирования — это самая «дорогая операция». Посмотри на бенчмарк, который я сделал: http://jsperf.com/catch-exception. Такая «сложная» на первый взгляд функция работает в 15-20 раз (а в Файрфоксе и вовсе в 50 раз) быстрее, чем перехват ошибки.
Цикл вместо метода
reduce()
работает ещё быстрее. Спасибо Роману Дворнову за дополнение к тесту.Если генерировать функции для каждого пути + кеш, то получается в 10 раз быстрее чем версия с reduce. А если не возникает исключения в рамках try/catch, то все работает ооочень быстро. http://jsperf.com/catch-exception/7
Вообще правильно тестировать на разных объектах и путях в рамках одного теста, скорей всего время припадет.
Перехват ошибок действительно очень дорогая операция, а её еще и могу не ловить или не поймать…
Давайте сделаем оговорку, что если путь не генерируется, а просто используется в коде, (например, мы добавили пути внутри Backbone-модели через точку), то вероятность поймать ошибку обратно-пропорциональна проценту покрытия кода тестами, верно?
Согласен с Романом — лучше формировать кеш из генерированных функций с хеш-функцией в виде запрошенного пути (Backbone-based):
Метод с reduce нужен только для
.set()
, если нужна опция воссоздания описанного пути внутри объекта, вместо исключения.Антон, спасибо за дополнение. Кеширование функций-аксессоров, безусловно, самый эффективный по скорости работы способ.
Если мы уж решили пойти по пути создания функции для доступа к свойствам объекта, то разумнее будет понять какая ситуация будет наиболее вероятна:
try
/catch
;Еще такие варианты есть: Раз: http://stackoverflow.com/questions/5484673/javascript-how-to-dynamically-create-nested-objects-using-object-names-given-by/11433067#11433067 Два: https://lodash.com/docs#get