Безопасный доступ к Javascript-объектам
У меня есть структура данных на основе json с объектами, содержащими вложенные объекты. Для доступа к определенному элементу данных я связывал ссылки на свойства объекта вместе. Например:
var a = b.c.d;
Если b или bc не определено, это приведет к ошибке с ошибкой. Тем не менее, я хочу получить значение, если оно существует иначе, просто не определено. Каков наилучший способ сделать это, не проверяя, что все значения в цепочке существуют?
Я хотел бы сохранить этот метод как можно более общим, поэтому мне не нужно добавлять огромное количество вспомогательных методов, таких как:
var a = b.getD();
или
var a = helpers.getDFromB(b);
Я также хочу попытаться избежать конструкций try/catch, поскольку это не ошибка, поэтому использование try/catch кажется неуместным. Это разумно?
Есть идеи?
Ответы
Ответ 1
Вы можете создать общий метод доступа к элементу на основе массива имен свойств, который интерпретируется как путь через свойства:
function getValue(data, path) {
var i, len = path.length;
for (i = 0; typeof data === 'object' && i < len; ++i) {
data = data[path[i]];
}
return data;
}
Тогда вы можете позвонить ему:
var a = getValue(b, ["c", "d"]);
Ответ 2
Стандартный подход:
var a = b && b.c && b.c.d && b.c.d.e;
довольно быстрый, но не слишком элегантный (особенно с более длинными именами свойств).
Использование функций для перемещения объектов объекта JavaScipt не является ни эффективным, ни элегантным.
Попробуйте это вместо этого:
try { var a = b.c.d.e; } catch(e){}
в случае, если вы уверены, что a
ранее не использовался или
try { var a = b.c.d.e; } catch(e){ a = undefined; }
в случае, если вы, возможно, назначили его раньше.
Это, вероятно, еще быстрее, чем первый вариант.
Ответ 3
Это старый вопрос, и теперь с функциями es6 эта проблема может быть решена более легко.
const idx = (p, o) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o);
Благодаря @sharifsbeat для этого решения.
Ответ 4
Если вы хотите иметь динамический доступ с нерегулярным количеством свойств под рукой, в ES6 вы можете легко сделать следующее:
function getNestedValue(o,...a){
var val = o;
for (var prop of a) val = typeof val === "object" &&
val !== null &&
val[prop] !== void 0 ? val[prop]
: undefined;
return val;
}
var obj = {a:{foo:{bar:null}}};
console.log(getNestedValue(obj,"a","foo","bar"));
console.log(getNestedValue(obj,"a","hop","baz"));
Ответ 5
вероятно, это может быть просто:
let a = { a1: 11, b1: 12, c1: { d1: 13, e1: { g1: 14 }}}
console.log((a || {}).a2); => undefined
console.log(((a || {}).c1 || {}).d1) => 13
и так далее.
Ответ 6
Возвращает значение по path
object
. Если разрешенное значение не undefined
, значение defaultValue
возвращается на свое место.
В ES6 мы можем получить вложенное свойство из Object
например, снизу кода.
const myObject = {
a: {
b: {
c: {
d: 'test'
}
}
},
c: {
d: 'Test 2'
}
},
isObject = obj => obj && typeof obj === 'object',
hasKey = (obj, key) => key in obj;
function nestedObj(obj, property, callback) {
return property.split('.').reduce((item, key) => {
if (isObject(item) && hasKey(item, key)) {
return item[key];
}
return typeof callback != undefined ? callback : undefined;
}, obj);
}
console.log(nestedObj(myObject, 'a.b.c.d')); //return test
console.log(nestedObj(myObject, 'a.b.c.d.e')); //return undefined
console.log(nestedObj(myObject, 'c.d')); //return Test 2
console.log(nestedObj(myObject, 'd.d', false)); //return false
console.log(nestedObj(myObject, 'a.b')); //return {"c": {"d": "test"}}
Ответ 7
Старый вопрос, и сейчас у нас так часто проекты Typescript, что этот вопрос кажется неуместным, но я попал сюда в поисках того же самого, поэтому я сделал для этого простую функцию. Ваши мысли о том, чтобы не использовать try/catch, на мой вкус слишком строги, ведь поиск undefined.x
все равно приведет к ошибке.
Итак, со всем этим, это мой метод.
function getSafe (obj, valuePath) {
try { return eval("obj." + valuePath); }
catch (err) { return null; }
}
Чтобы использовать это, мы должны передать объект. Я пытался избежать этого, но не было другого способа получить доступ к нему из другой функции (здесь есть куча вопросов по этому поводу).
И небольшой набор тестов, чтобы увидеть, что мы получаем:
let outsideObject = {
html: {
pageOne: {
pageTitle: 'Lorem Ipsum!'
}
}
};
function testme() {
let insideObject = { a: { b: 22 } };
return {
b: getSafe(insideObject, "a.b"), // gives: 22
e: getSafe(insideObject, "a.b.c.d.e"), // gives: null
pageTitle: getSafe(outsideObject, "html.pageOne.pageTitle"), // gives: Lorem Ipsum!
notThere: getSafe(outsideObject, "html.pageOne.pageTitle.style") // gives: undefined
}
}
testme();
UPDATE:
Что касается использования eval
, я думаю, что eval - это инструмент, который нужно использовать осторожно, а не сам дьявол. В этом методе пользователь не вмешивается в eval, поскольку именно разработчик ищет свойство по его имени.