Подтвердить что ты не робот

Как избежать "невозможно прочитать свойство ошибок undefined"?

В моем коде, я имею дело с массивом, который имеет некоторые записи с множеством объектов, вложенных друг в друга, где, как некоторые нет. Это выглядит примерно так:

// where this array is hundreds of entries long, with a mix
// of the two examples given
var test = [{'a':{'b':{'c':"foo"}}}, {'a': "bar"}];

Это дает мне проблемы, потому что мне нужно время от времени перебирать массив, а несогласованность вызывает такие ошибки:

for (i=0; i<test.length; i++) {
    // ok on i==0, but 'cannot read property of undefined' on i==1
    console.log(a.b.c);
}

Я знаю, что могу сказать if(a.b){ console.log(a.b.c)}, но это необычайно утомительно в тех случаях, когда есть до 5 или 6 объектов, вложенных друг в друга. Есть ли какой-либо другой (более простой) способ, которым я могу его использовать ТОЛЬКО в console.log, если он существует, но без ошибки?

4b9b3361

Ответ 1

Быстрый обходной путь - использование вспомогательной функции try/catch с функцией стрелки ESa arrow function:

function getSafe(fn, defaultVal) {
    try {
        return fn();
    } catch (e) {
        return defaultVal;
    }
}

// use it like this
getSafe(() => obj.a.lot.of.properties);

// or add an optional default value
getSafe(() => obj.a.lot.of.properties, 'nothing');

Рабочий фрагмент:

function getSafe(fn, defaultVal) {
  try {
    return fn();
  } catch (e) {
    return defaultVal;
  }
}

// use it like this
console.log(getSafe(() => obj.a.lot.of.properties));

// or add an optional default value
console.log(getSafe(() => obj.a.lot.of.properties, 'nothing'));

Ответ 2

Что вы делаете, возникает исключение (и это справедливо).

Вы всегда можете сделать

try{
   window.a.b.c
}catch(e){
   console.log("YO",e)
}

Но я бы не подумал о вашем случае использования.

Почему вы получаете доступ к данным, 6 уровней вложенных, которые вы не знакомы? Какой вариант использования оправдывает это?

Обычно вы хотите проверить, с какими объектами вы имеете дело.

Кроме того, на стороне примечания вы не должны использовать такие выражения, как if(a.b), потому что он вернет false, если a.b равно 0 или даже если это "0". Вместо этого проверьте, если a.b !== undefined

Ответ 3

Если я правильно понимаю ваш вопрос, вам нужен самый безопасный способ определить, содержит ли объект свойство.

Самый простой способ - использовать инструкцию "in".

window.a = "aString";
//window should have 'a' property
//lets test if it exists
if ("a" in window){
    //true
 }

if ("b" in window){
     //false
 }

Конечно, вы можете вложить это настолько глубоко, насколько хотите

if ("a" in window.b.c) { }

Не уверен, что это помогает.

Ответ 4

Если вы используете lodash, вы можете использовать их функцию "есть". Он похож на нативный "in", но позволяет пути.

var testObject = {a: {b: {c: 'walrus'}}};
if(_.has(testObject, 'a.b.c')) {
  //Safely access your walrus here
}

Ответ 5

Попробуй это. Если a.b не определено, он оставит оператор if без каких-либо исключений.

if (a.b && a.b.c) {
  console.log(a.b.c);
}

Ответ 6

Это обычная проблема при работе с глубоким или сложным json-объектом, поэтому я стараюсь избегать try/catch или встраивания нескольких проверок, которые сделают код нечитаемым, я обычно использую этот маленький кусок кода во всех своих процессах задание.

/* ex: getProperty(myObj,'aze.xyz',0) // return myObj.aze.xyz safely
 * accepts array for property names: 
 *     getProperty(myObj,['aze','xyz'],{value: null}) 
 */
function getProperty(obj, props, defaultValue) {
    var res, isvoid = function(x){return typeof x === "undefined" || x === null;}
    if(!isvoid(obj)){
        if(isvoid(props)) props = [];
        if(typeof props  === "string") props = props.trim().split(".");
        if(props.constructor === Array){
            res = props.length>1 ? getProperty(obj[props.shift()],props,defaultValue) : obj[props[0]];
        }
    }
    return typeof res === "undefined" ? defaultValue: res;
}

Ответ 7

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

Ответ 8

Мне нравится ответ Цао Шоугуана, но я не люблю передавать функцию в качестве параметра в функцию getSafe каждый раз, когда я выполняю вызов. Я изменил функцию getSafe, чтобы принимать простые параметры и чистый ES5.

/**
* Safely get object properties.    
* @param {*} prop The property of the object to retrieve
* @param {*} defaultVal The value returned if the property value does not exist
* @returns If property of object exists it is returned, 
*          else the default value is returned.
* @example
* var myObj = {a : {b : 'c'} };
* var value;
* 
* value = getSafe(myObj.a.b,'No Value'); //returns c 
* value = getSafe(myObj.a.x,'No Value'); //returns 'No Value'
* 
* if (getSafe(myObj.a.x, false)){ 
*   console.log('Found')
* } else {
*  console.log('Not Found') 
* }; //logs 'Not Found'
* 
* if(value = getSafe(myObj.a.b, false)){
*  console.log('New Value is', value); //logs 'New Value is c'
* }
*/
function getSafe(prop, defaultVal) {
  return function(fn, defaultVal) {
    try {
      if (fn() === undefined) {
        return defaultVal;
      } else {
        return fn();
      }
    } catch (e) {
      return defaultVal;
    }
  }(function() {return prop}, defaultVal);
}

Ответ 9

В ответе str значение "undefined" будет возвращено вместо установленного значения по умолчанию, если свойство не определено. Иногда это может вызвать ошибки. Следующее будет гарантировать, что defaultVal всегда будет возвращен, когда свойство или объект не определены.

const temp = {};
console.log(getSafe(()=>temp.prop, '0'));

function getSafe(fn, defaultVal) {
    try {
        if (fn() === undefined) {
            return defaultVal
        } else {
            return fn();
        }

    } catch (e) {
        return defaultVal;
    }
}

Ответ 10

Если вы используете Babel, вы уже можете использовать необязательный синтаксис цепочки с плагином Babel @babel/plugin-offer-option-chaining. Это позволит вам заменить это:

console.log(a && a.b && a.b.c);

с этим:

console.log(a?.b?.c);

Ответ 11

В Lodash есть метод get который допускает использование по умолчанию в качестве необязательного третьего параметра, как показано ниже:

const myObject = {
  has: 'some',
  missing: {
    vars: true
  }
}
const path = 'missing.const.value';
const myValue = _.get(myObject, path, 'default');
console.log(myValue) // prints out default, which is specified above
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Ответ 12

Если у вас есть lodash, вы можете использовать метод .get

_.get(a, 'b.c.d.e')

или дать ему значение по умолчанию

_.get(a, 'b.c.d.e', default)

Ответ 13

Представьте, что мы хотим применить ряд функций к x тогда и только тогда, когда x не равен нулю:

if (x !== null) x = a(x);
if (x !== null) x = b(x);
if (x !== null) x = c(x);

Теперь давайте скажем, что нам нужно сделать то же самое с y:

if (y !== null) y = a(y);
if (y !== null) y = b(y);
if (y !== null) y = c(y);

И то же самое с z:

if (z !== null) z = a(z);
if (z !== null) z = b(z);
if (z !== null) z = c(z);

Как вы можете видеть без надлежащей абстракции, мы будем в конечном итоге дублировать код снова и снова. Такая абстракция уже существует: монада Maybe.

Монада Maybe содержит как значение, так и вычислительный контекст:

  1. Монада сохраняет значение в безопасности и применяет к нему функции.
  2. Вычислительный контекст является нулевой проверкой перед применением функции.

Наивная реализация будет выглядеть следующим образом:

Implementation Эта реализация только для иллюстрации! Это не то, как это должно быть сделано, и это неправильно на многих уровнях. Однако это должно дать вам лучшее представление о том, о чем я говорю.

Как видите, ничего не может сломаться:

  1. Мы применяем ряд функций к нашему значению
  2. Если в какой-то момент значение становится нулевым (или неопределенным), мы просто больше не применяем никаких функций.

const abc = obj =>
  Maybe
    .of(obj)
    .map(o => o.a)
    .map(o => o.b)
    .map(o => o.c)
    .value;

const values = [
  {},
  {a: {}},
  {a: {b: {}}},
  {a: {b: {c: 42}}}
];

console.log(

  values.map(abc)

);
<script>
function Maybe(x) {
  this.value = x; //-> container for our value
}

Maybe.of = x => new Maybe(x);

Maybe.prototype.map = function (fn) {
  if (this.value == null) { //-> computational context
    return this;
  }
  return Maybe.of(fn(this.value));
};
</script>

Ответ 14

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

function containsProperty(instance, propertyName) {
    // make an array of properties to walk through because propertyName can be nested
    // ex "test.test2.test.test"
    let walkArr = propertyName.indexOf('.') > 0 ? propertyName.split('.') : [propertyName];

    // walk the tree - if any property does not exist then return false
    for (let treeDepth = 0, maxDepth = walkArr.length; treeDepth < maxDepth; treeDepth++) {

        // property does not exist
        if (!Object.prototype.hasOwnProperty.call(instance, walkArr[treeDepth])) {
            return false;
        }

        // does it exist - reassign the leaf
        instance = instance[walkArr[treeDepth]];

    }

    // default
    return true;

}

В вашем вопросе вы можете сделать что-то вроде:

let test = [{'a':{'b':{'c':"foo"}}}, {'a': "bar"}];
containsProperty(test[0], 'a.b.c');

Ответ 15

Я иногда пишу это так:

console.log((a.b || {}).c);

Это, вероятно, более эффективно, чем try-catch, хотя и менее читабельно.

Ответ 16

Я обычно использую так:

 var x = object.any ? object.any.a : 'def';