Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid "the property of undefined" error without having huge if statement in JavaScript?

I usually find myself working with deep objects like this:

var x = {
  y: {
    z: {
      a:true
    }
  }
}

And somewhere in the code:

if( x.y.z.a === true ){
  //do something
}

And in some cases any of the x,y,z variables could be undefined, in which case you would get "Cannot read property * of undefined"

Potential solution is:

if( x && x.y && x.y.z && x.y.z.a === true ){
  //do something
}

jsfiddle: http://jsfiddle.net/EcFLk/2/

But is there any easier/shorter way? Inline solutions (without using special function) would be great. Thanks.

like image 440
Sherzod Avatar asked Mar 08 '12 22:03

Sherzod


2 Answers

This works :P

if ( $($($(x).prop('y')).prop('z')).prop('a') ) {
    // code
}

Live demo: http://jsfiddle.net/Yw5th/

It's an ugly pattern, but at least it's an one-liner and the property names don't have to be repeated (unlike x && x.y && x.y.z && ...).

like image 41
Šime Vidas Avatar answered Sep 27 '22 23:09

Šime Vidas


Nope, you've already found the right way. Of course, you can use a try/catch block and handle the error after-the-fact, but I'd use the x && x.y && x.y.z && x.y.z.a solution.

(You don't need the === true unless you really want the condition to only be true when a is strictly equal to true and not when it's 1 or "hi", but from your question, I'm thinking you know that already.)


You've said you don't want to use a function for this, and I haven't felt the need for one either, but just for fits and giggles:

function ref(obj, names) {
    var rv = obj, index;
    if (names) {
        for (index = 0; rv && index < names.length; ++index) {
            rv = rv[names[index]];
        }
    }
    return rv;
}

Usage:

if (ref(x, ["y", "z", "a"]) === true) {
    // do something
}

Function calls are so cheap these days...

Or alternately:

function ref(obj) {
    var rv = obj, index;
    for (index = 1; rv && index < arguments.length; ++index) {
        rv = rv[arguments[index]];
    }
    return rv;
}

Usage:

if (ref(x, "y", "z", "a") === true) {
    // do something
}

...but on most JavaScript engines, that will be slower (arguments tends to be slow). But then again, you'd have to be doing it thousands of times in a loop for the speed to be an issue.

Or as Šime suggests, a single variable (I was avoiding the split, but it's not expensive):

function ref(obj, path) {
    var rv = obj, names = path.split("."), index;
    for (index = 0; rv && index < names.length; ++index) {
        rv = rv[names[index]];
    }
    return rv;
}

Usage:

if (ref(x, "y.z.a") === true) {
    // do something
}

Live example of all three | Live source

like image 90
T.J. Crowder Avatar answered Sep 27 '22 22:09

T.J. Crowder