6

I'd like to do this:

if(a.b.c) alert('c exists')   //produces error
if(a && a.b && a.b.c ) alert('c exists')   //also produces ReferenceError

The only way I know of to do this (EDIT: This apparently is the only way):

if(typeof(a) != "undefined" && a.b && a.b.c) alert('c exists');

or some type of function like this...

if(exists('a.b.c')) alert('c exists');
function exists(varname){
    vars=varname.split('.');
    for(i=0;i<vars.length;i++){
       //iterate through each object and check typeof
    }
}
//this wont work with local variables inside a function

EDIT: SOLUTION BELOW (Credit to this thread by Felix, I just adapted it a little Check if object member exists in nested object)

This works:

if (typeof a != 'undefined' && a.b && a.b.c) alert('c exists')

But the best thing I found is to put it into a function. I use 2 different functions, one to get a variable deep in an object, and one just to check if its set.

/**
 * Safely retrieve a property deep in an object of objects/arrays
 * such as userObj.contact.email
 * @usage var email=getprop(userObj, 'contact.email')
 *      This would retrieve userObj.contact.email, or return FALSE without
 *      throwing an error, if userObj or contact obj did not exist
 * @param obj OBJECT - the base object from which to retrieve the property out of
 * @param path_string STRING - a string of dot notation of the property relative to
 * @return MIXED - value of obj.eval(path_string), OR FALSE
 */
function getprop(obj, path_string)
{
    if(!path_string) return obj
    var arr = path_string.split('.'),
        val = obj || window;

    for (var i = 0; i < arr.length; i++) {
        val = val[arr[i]];
        if ( typeof val == 'undefined' ) return false;
        if ( i==arr.length-1 ) {
            if (val=="") return false
            return val
        }
    }
    return false;
}

/**
 * Check if a proprety on an object exists
 * @return BOOL
 */
function isset(obj, path_string)
{
    return (( getprop(obj, path_string) === false ) ? false : true)
}
Community
  • 1
  • 1
timh
  • 1,572
  • 2
  • 15
  • 25
  • No, the custom function is the best you can get. I think I already answered a similar question, but I cannot find it ;) – Felix Kling Feb 19 '11 at 21:15
  • 1
    Just a side note, typeof is not a function, so you don't need the parens. You can just say `if (typeof a !== 'undefined' && ...` – Eli Feb 19 '11 at 21:22
  • What is your code doing trying to access the interstices of a variable when you don't even know that the variable exists. Quite apart from contravention of the Law of Demeter, it seems dubious to me (a non-Javascript programmer) to be trying to access the variable in the first place. – Jonathan Leffler Feb 19 '11 at 21:28
  • `x.y` <-- `y` is a *property* of the object stored in `x`. That is, `y` is not a *variable*. *Variables* can only be accessed by their name in the applicable lexical scope. –  Feb 19 '11 at 21:32
  • The "ReferenceError" occurs because the variable `a` is not defined (nor is it a property of the global [`window`] object). "Declare" it with `var` first and/or assign it a value (if you just assign a value a new window property -- global -- will be created if needed). All the answers below are correct. –  Feb 19 '11 at 21:37
  • 1
    possible duplicate of [Check if object member exists in nested object](http://stackoverflow.com/questions/4676223/check-if-object-member-exists-in-nested-object) – Felix Kling Feb 19 '11 at 23:41
  • @JonathanLeffler - if for exmaple, a json object is coming back from an ajax call... such as userObj.contact.home_phone. The user may or may not have set a home_phone... – timh May 08 '12 at 22:56
  • Note that the function above can be augmented to address bracket access (e.g. `a.b[3]` or `a.b[c]` ) as well – G0BLiN Mar 26 '17 at 08:45

3 Answers3

6

Try this:

if (a && a.b && a.b.c)
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • try it. im doing it in chrome now: `if (a && a.b && a.b.c) alert(1) ReferenceError: a is not defined` THIS works... but still annoying: `if (typeof(a)!="undefined" && a.b && a.b.c) alert(1)` – timh Feb 19 '11 at 21:19
  • Where is `a` coming from? Joel's code is legit and should work. – Eli Feb 19 '11 at 21:21
  • assume for this example, its a global variable. but remember since im testing if a.b.c exists, it could very well be that "a" doesnt even exist in the first place. EDIT: just tested a local variable in a function scope and got same result. – timh Feb 19 '11 at 21:23
  • @timh The assumption is wrong -- the ReferenceError occurs because it is *not* a property on the window object as is *not* a variable. Fix that. Either that or some *other* code is throwing the error. –  Feb 19 '11 at 21:40
  • @timh It does. **Your assumptions are incorrect.** `var a = "it works"; if (a && a.length) { alert(a) }` –  Feb 19 '11 at 21:41
  • 1
    This solution would prevent you passing any value that evaluates to false, such as null, 0 etc. The typeof operator should be used, either directly or encapsulated into another function. – leebriggs Feb 19 '11 at 21:56
  • True. this is not the correct solution. I edited it, but someone edited it back, so I will post it into my original answer at the bottom. – timh Feb 21 '11 at 05:18
3

How about this:

function exists(str, namespace) {
    var arr = str.split('.'),
        val = namespace || window;

    for (var i = 0; i < arr.length; i++) {
        val = val[arr[i]];
        if ( typeof val == 'undefined' ) return false;
    }
    return true;    
}

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

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • Thanks for working this out for me. i was hoping to avoid this, but I guess its the only way. – timh Feb 19 '11 at 21:38
  • Depending on your use, you should also balance code structure with performance. Whilst verbose, the typeof operator is fast. – leebriggs Feb 19 '11 at 21:58
  • @timh My solution does not work well. If, for instance, you call the exists() function from inside a function context, then you won't be able to evaluate the local variables of that function (unless you are able to pass the local namespace into the exists() function). See here: http://jsfiddle.net/MdWEH/ – Šime Vidas Feb 19 '11 at 22:20
  • ha. yea i was just going to mention that. im not sure how to pass a local functions namespace into the variable... any ideas? – timh Feb 19 '11 at 22:22
  • @timh I don't think that's possible. If I remember correctly, the local namespace of a function is represented by the function's Variable object which cannot be accessed programmatically. – Šime Vidas Feb 19 '11 at 22:34
0

How about double-banging the evaluation?

if (!!a && !!a.b && !!a.b.c)
Eli
  • 17,397
  • 4
  • 36
  • 49
  • Sorry Reid, but that's not true. Just run `!!undefined` from a console and you will see it return `false`. – Eli Feb 19 '11 at 21:31
  • Try running !!xyz, and you dont get false, you get error. I just ran the "if" statement you suggested in chrome js console on ubuntu, and got ReferenceError also "a is not defined" – timh Feb 19 '11 at 21:34
  • The problem isn't with the `.b` or `.b.c` it is with the `a`. Property-access can return undefined, but will *never* throw an error (unless overloaded). –  Feb 19 '11 at 21:35
  • @timh Because **a is not defined** Try: `var a = 1; if (a && a.b && a.b.c) { .. }` Note that there *is* an `a` defined in that snippet. –  Feb 19 '11 at 21:36
  • right but that defeats the purpose doesnt it? That would overwrite my variable a, if it existed. – timh Feb 19 '11 at 21:37
  • @timh *Don't use* a variable (or "global variable") you *don't have*. Statically typed languages would trigger this error at compile-time. If you may-or-may not have it: `window.a && window.a.b ...` Note that now `a` is accessed *as a property*. –  Feb 19 '11 at 21:42
  • @pst ok I got you. but even so... doing the whole (a && a.b && a.b.c) is annoying, but if I must then ok. The 2nd question with the exists() function I guess would be the best solution assuming I didn't want to didn't want to hassle with the .. && .. && .. method. – timh Feb 19 '11 at 21:49