Безопасный доступ к 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, поскольку именно разработчик ищет свойство по его имени.