Avoiding the error "Can not read property 'c' of undefined"

8

Considering the following code:

var a = {};

if (a.b.c !== undefined) {
  console.log("definido!");
} else {
  console.log("indefinido!");
}

Is there any way to check each property without having to test one by one?

What I want is the result of a if similar to the following but simplified:

var a = {};

if (a !== undefined && a.b !== undefined && a.b.c !== undefined) {
  console.log("definido!");
} else {
  console.log("indefinido!");
}
    
asked by anonymous 20.02.2017 / 19:20

4 answers

9

I do not like this solution, but can catch the exception that will be generated because of the error:

var a = {};

try {
    console.log(a.b.c);
} catch (e) {
    console.log("indefinido");
}

If you just want to simplify without deleting the individual check, you can use short-circuit , if it is just nesting of complex objects, if any of the nested checked members are scalars it can give false negative:

var a = {};

if (a && a.b && a.b.c) {
    console.log("definido!");
} else {
    console.log("indefinido!");
}

a = { b : { c: {} } };

if (a && a.b && a.b.c) {
    console.log("definido!");
} else {
    console.log("indefinido!");
}

You can also create an abstraction. You can create a function or use a ready one that does the heavy lifting.

There are a few more possibilities, but it's too much of a problem.

    
20.02.2017 / 19:31
8

The name of this resource is null propagation and this does not exist in JavaScript.

You can use lodash to do something similar. It has a function called get , which is parameterized by the object, the properties you want to access, and the value default for return if the specified properties can not be found.

// Exemplo onde a propriedade não existe

var a = {};
var result = _.get(a, 'b.c', 'Indefinido');
console.log(result);

// Exemplo onde a propriedade existe

var obj = { innerObj: { innerObj1: 'Teste' } };
var valor = _.get(obj, 'innerObj.innerObj1', 'Indefinido');
console.log(valor);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
    
20.02.2017 / 19:31
7

I ended up using the information from the 3 answers proposed so far. Reading the response from @jbueno I saw that the resource name is null propagation and that although it does not exist in Javascript, it has great chances of being implemented in the near future.

Based on the above principle, I took into account the response of @RobertoFagundes and decided to use a function for it.

Viewing the @bigown response I used the concepts that he presented and I came up with the following temporary resolution (until implemented in Javascript):

Object.prototype.hasProperty = function(signature) {
  var finalProperty = this;
  var property;
  var child;

  if (signature === undefined) {
    return true;
  }

  if (signature instanceof String) {
    signature = signature.split('.');
  }

  if (signature.constructor !== Array) {
    signature = [signature];
  }

  if (signature.length === 0) {
    return true;
  }

  property = signature.shift();
  child = this[property]; 

  if (child === undefined) {
    return false;
  } else {
    return child.hasProperty(signature);
  }
}

var a = {b: {c: {}}};

if (a.hasProperty(['b', 'c'])) {
  console.log("definido!");
} else {
  console.log("indefinido!");
}
    
21.02.2017 / 05:23
5

You can use a recursive function to do this job, like this:

function verificarArrayUnsigned(array){
  var isUnsigned = false;
  if (typeof array !== "unsigned"){
    for (var i = 0; i < array.length; i++){
      if (typeof array[i] === "array"){
        isUnsigned = verificarArrayUnsigned(array[i]);
      } else {
        if (typeof array[i] === "unsigned"){
          isUnsigned = true;
        }
      }
      if (isUnsigned){
        break;
      }
    }
  } else {
    isUnsigned = true;
  }
  
  return isUnsigned;
}

In this function you can pass both an array and a variable.

    
20.02.2017 / 19:55