3

Time and again I have to deal with code like consider following hypothetical example:

if (node.data.creatures.humans.women.number === Infinity) {
  // do-something
}

The problem is that if the node is undefined, this condition will break. Similarly, it will break if node.data is undefined, node.data.creatures is undefined, and so on.

So I end up using following kind of long condition:

if (node && node.data && node.data.creatures && node.data.creatures.humans && node.data.creatures.women && node.data.creatures.humans.women.number === Infinity) {
  // do-something
}

Now, imagine I have to use parts of that JSON object in many other parts of code too. The code suddenly starts looking very ugly.

Is there another way of avoiding errors like "Cannot call property of undefined", etc., due to the first condition I mentioned (such that the code looks better too)?

How do you deal with such situations?

Uthman
  • 9,251
  • 18
  • 74
  • 104
  • 1
    The new Proxy API (quite new and support is limited) can easily solve this: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Proxy – haim770 Mar 07 '16 at 08:25
  • As a note, it is a JavaScript object and not a _JSON object_. JSON representation of data, if you parse that data in JSON format you will get a JavaScript object as result. – t.niese Mar 07 '16 at 08:25
  • 1
    Nowadays this is trivial thanks to [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining): `if (node?.data?.creatures?.humans?.women?.number === Infinity)` – CherryDT May 24 '23 at 20:55
  • You are right @CherryDT. This question is quite old. – Uthman May 25 '23 at 01:08

3 Answers3

1

Here is what i'm using, it may not be the "optimizest" way, but, that works for me:

walkObject = function(obj, path) {
  var i = 0,
    part = void 0,
    parts = path.split('.');

  while (obj && (part = parts[i++])) {
    obj = obj[part];
  }
  return obj;
};

The method takes an object, and string containing a dot notation of the property you want to test:

// the object
var obj = { a: { b: {c: [1,2,3], d: "aa"} } };

// tests
console.log("a,b,c", walkObject(obj, 'a.b.c')); // [1,2,3]
console.log("a,b,e", walkObject(obj, 'a.b.e')); // undefined
console.log("z,b,e", walkObject(obj, 'z.b.e')); // undefined
cl3m
  • 2,791
  • 19
  • 21
0

Trying to access a property on undefined is a catchable TypeError, so you can use try..catch:

try {
    if (node.data.creatures.humans.women.number === Infinity) {
        // do something
    }
} catch (e) {
    // property doesn't exist
}
deceze
  • 510,633
  • 85
  • 743
  • 889
0

You can wrap the potentially harmful parts with try - catch:

try {
    if (node.data.creatures.humans.women.number === Infinity) {
        // do-someting
    }
} catch () {
    console.log(e);
}

Another way and maybe a little more elegant would be defining the expected object structure and them merging it with the actual data using for example jQuery.extend().

var nodeTemplate = {
    data: {
        creatures: {
            humans: {...}
        }
    }
}

This will make sure that the structure is as you expect and non-existent fields have some default values.

Then merge it:

node = $.extend(nodeTemplate, node);
martin
  • 93,354
  • 25
  • 191
  • 226