26

I have to check deeply-nested object property such as YAHOO.Foo.Bar.xyz.

The code I'm currently using is

if (YAHOO && YAHOO.Foo && YAHOO.Foo.Bar && YAHOO.Foo.Bar.xyz) { // operate on YAHOO.Foo.Bar.xyz } 

This works, but looks clumsy.

Is there any better way to check such deeply nested property?

7
  • 3
    Can you not just do if (YAHOO.Foo.Bar.xyz)? Commented Aug 3, 2011 at 13:33
  • 1
    enterprise-js.com/34 <-- is a joke btw Commented Aug 3, 2011 at 13:41
  • 6
    @Michael, you can't just do if (YAHOO.Foo.Bar.xyz) because if Foo or Bar doesn't exist, then if (YAHOO.Foo.Bar.xyz) will throw an exception because of the dereference of the intermediate values that are undefined. Commented Aug 3, 2011 at 13:46
  • 1
    This is probably the best way to check; using a try...catch will impact performance - when an exception is thrown, the code will slow down. jsPerf test. Commented Aug 3, 2011 at 14:05
  • 1
    @Digital Plane - But, if the normal case is that it exists, then the try/catch is 19x faster than the multiple if tests (in Chrome). Commented Aug 3, 2011 at 15:37

7 Answers 7

20

If you expect YAHOO.Foo.Bar to be a valid object, but want to make your code bulletproof just in case it isn't, then it can be cleanest to just put a try catch around it and let one error handler catch any missing segment. Then, you can just use one if condition instead of four that will detect if the terminal property exists and a catch handler to catch things if the intermediate objects don't exist:

try { if (YAHOO.Foo.Bar.xyz) { // operate on YAHOO.Foo.Bar.xyz } catch(e) { // handle error here } 

or, depending upon how your code works, it might even just be this:

try { // operate on YAHOO.Foo.Bar.xyz } catch(e) { // do whatever you want to do when YAHOO.Foo.Bar.xyz doesn't exist } 

I particularly use these when dealing with foreign input that is supposed to be of a particular format, but invalid input is a possibility that I want to catch and handle myself rather than just letting an exception propagate upwards.

In general, some javascript developers under-use try/catch. I find that I can sometimes replace 5-10 if statements checking input with a single try/catch around a larger function block and make the code a lot simpler and more readable at the same time. Obviously, when this is appropriate depends upon the particular code, but it's definitely worth considering.

FYI, if the usual operation is to not throw an exception with the try/catch, it can be a lot faster than a bunch of if statements too.


If you don't want to use the exception handler, you can create a function to test any arbitrary path for you:

function checkPath(base, path) { var current = base; var components = path.split("."); for (var i = 0; i < components.length; i++) { if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) { return false; } current = current[components[i]]; } return true; } 

Example usage:

var a = {b: {c: {d: 5}}}; if (checkPath(a, "b.c.d")) { // a.b.c.d exists and can be safely accessed } 
Sign up to request clarification or add additional context in comments.

11 Comments

Don't abuse try catch like that. Dear god my eyes. And even an empty catch block. This is a code smell if I've ever seen one.
@Raynos - Why is this abusing try/catch? It makes more sense to me than multiple nested if statements to check all the intermediate levels. An empty catch just means you purposely want to do nothing if the intermediate levels don't exist (a reasonable design choice). This is perfectly fine code. Did you actually downvote because you prefer a different approach? There is absolutely nothing wrong with using try/catch like this.
"the occurrence of exceptions, special conditions that change the normal flow of program execution." I find it a massive stretch to claim that the property not existing is a "special condition that changes the normal flow of program execution". I think your using the wrong tool for the job.
An intermediate property not existing when you normally expect it to exist can very well be "a special condition that changes the normal flow of program execution" and seems a perfect use for exceptions. You seem to have an opinion that exceptions should only be used for extraordinary things, but they are just another error return/propagation mechanism that can be used much more efficiently than multiple if statements in many circumstances, particularly when you are dealing with foreign input that you don't necessarily control and it could be very expensive to test every single operation.
I use exceptions all them all the time when operating on foreign HTML (HTML not under my control) that is supposed to adhere to a particular format, but might have errors in it. It's particularly useful when there's a bunch of housekeeping in the beginning (parsing structure, obtaining data, etc...) and then the final result is applied at the end. I surround the whole block with a try/catch and if any of the structure isn't as expected, it throws an error and I catch all errors in one place very efficiently rather than using dozens of if statements. Makes the code much more readable too.
|
6
var _ = {}; var x = ((YAHOO.Foo || _).Bar || _).xyz; 

1 Comment

Thumbs up for the most unclear line this week.
6

Consider this utility function:

function defined(ref, strNames) { var name; var arrNames = strNames.split('.'); while (name = arrNames.shift()) { if (!ref.hasOwnProperty(name)) return false; ref = ref[name]; } return true; } 

Usage:

if (defined(YAHOO, 'Foo.Bar.xyz')) { // operate on YAHOO.Foo.Bar.xyz } 

Live demo: http://jsfiddle.net/DWefK/5/

5 Comments

I think you need an explicit check for undefined in the while loop test, rather than just a truthiness test. At the moment defined({a:''}, 'a.b') === true. Nice approach though, I like it!
@codebox I wasn't satisfied with my code so I did a rewrite. What do you think?
I would love to have something like this in underscore or lodash
Oh lodash actually has it. _.get(), awesome! Even has a default parameter.
It's a very nice solution. But if the reference object is undefined the code breaks. I think a check if the reference object is defined or not will solve the problem. Your solution, Updated one
3

If you need to check the correctness of the path, rather than the existance of the "xyz" member on the "YAHOO.Foo.Bar" object, it will probably be easiest to wrap the call in a try catch:

var xyz; try { xyz = YAHOO.Foo.Bar.xyz; } catch (e) { // fail; }; 

Alternately, you can do some string-kong-fu-magicTM:

function checkExists (key, obj) { obj = obj || window; key = key.split("."); if (typeof obj !== "object") { return false; } while (key.length && (obj = obj[key.shift()]) && typeof obj == "object" && obj !== null) ; return (!key.length && typeof obj !== "undefined"); } 

The use as follows:

if (checkExists("YAHOO.Foo.Bar.xyz")) { // Woo! }; 

7 Comments

When would you EVER use everything involved in the checkExists function rather than the try/catch method?
I just don't see the point of the typeof obj !== "object" tests. Why be restrictive?
@jfriend00: The string method allows you to find the point where things break down (if you happen to want that as well). Passing object descriptions via string also makes sense in some cases, like custom module systems.
@missingno: It checks the provided "obj" by the user (which is optional; it defaults to window if it isn't provided), is actually an object (it stops the function breaking if the user passes the wrong argument, basically!)
@jfriend00: The try/catch is a bit to type if you've got a lot of places where you want to check the existence of an object path.
|
1

This problem is solved quite beautifully by coffeescript (which compiles down to javascript):

if YAHOO.Foo?.Bar?.xyz // operate on YAHOO.Foo.Bar.xyz 

Comments

0

use a try catch.

a={ b:{} }; //a.b.c.d?true:false; Errors and stops the program. try{ a.b.c.d; } catch(e){ console.log(e);//Log the error console.log(a.b);//This will run } 

Comments

0

I actually voted to close the question as duplicate of javascript convert dotnotation string into objects.

However, I guess it's a different topic, but the answer there might still be helpful if you don't want to try-catch all the time.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.