4

I'm working in a large Javascript codebase at the moment littered with code resorting to Exceptions for flow control

function getChecklistUrl() {
    try {
        return dataLayerObject.config.checklist;
    } catch (e) {
        try {
            console.error('dataLayer', e);
        } catch (ignore) {}
    }
}

I might favor conditional logic such as this implementation of the same function

function getChecklistUrl() {
    if(typeof dataLayerObject == 'object'        &&
       'config' in dataLayerObject               &&
       typeof dataLayerObject.config == 'object' &&
       'checklist' in dataLayerObject.config     &&
       typeof dataLayerObject.config.checklist == 'object') {
        return dataLayerObject.config.checklist;
    }
    return null;
}

While the later feels longwinded, a helper function could certainly be written to reduce the boilerplate for such checks.

Is the former then idiomatic for Javascript? Is the later brittle (across browsers / scenarios) and better left to a try / catch anyway? Or is the former simply evidence of laziness?

Edit

these objects are presumed to be 'plain' objects, such as var obj = {} so I don't believe I care about the prototype chain here.

11
  • 1
    'config' in dataLayerObject is redundant if directly followed by typeof dataLayerObject.config == 'object' (would otherwise be undefined) Commented Nov 12, 2015 at 16:12
  • 1
    also, one thing to note, typeof null === 'object' in ES5 and bellow Commented Nov 12, 2015 at 16:16
  • @Amit not true, assume var dataLayerObject = { config: null }; Then 'config' in dataLayerObject evaluates to true, there for a subsequent check for dataLayerObject.config.something will throw an exception... Commented Nov 12, 2015 at 16:17
  • @nem That is a very good point! Commented Nov 12, 2015 at 16:18
  • Are you trying to find properties only on the object, or on the prototype chain as well? Seems from your code (by using in) that you are looking for prototype methods as well? Commented Nov 12, 2015 at 16:20

4 Answers 4

6

The proper way to check for object properties in javascript is the Object.hasOwnProperty() method.

example:

var Person = {
  first_name: 'Fred',
  last_name: 'Flintstone'
};

if ( 'object' === typeof Person && Person.hasOwnProperty('first_name' ) {
  window.alert('the property exists!');
}

EDIT

for checking for nested object properties, you can try something like this:

function checkNested(obj /*, level1, level2, ... levelN*/) {
  var args = Array.prototype.slice.call(arguments, 1);

  for (var i = 0; i < args.length; i++) {
    if (!obj || !obj.hasOwnProperty(args[i])) {
      return false;
    }
    obj = obj[args[i]];
  }
  return true;
}

var test = {level1:{level2:{level3:'level3'}} };

checkNested(test, 'level1', 'level2', 'level3'); // true
checkNested(test, 'level1', 'level2', 'foo'); // false
Sign up to request clarification or add additional context in comments.

2 Comments

That's wrong. hasOwnProperty() ignores prototype chain.
Thanks for this. In the end I found @nem's answer a little more helpful, but appreciate your answer and have given you an upvote.
2

First of all, you don't need to check for both property in object && typeof obj[property] == 'object', you can only use the typeof to take care of both checks. The reason is that, if that obj.property doesn't exist, typeof will return undefined.

Therefore, you can modularize your approach by writing a small utility function that checks if something is an object:

function isObject(o) {
    return typeof o == 'object' && o !== null; // take care of the corner case that typeof null == 'object'
}

Then just use it on your necessary object chain to find a property by checking that all of its owning objects exists:

function getChecklistUrl() {
    if(isObject(dataLayerObject) && 
       isObject(dataLayerObject.config) &&  
       isObject(dataLayerObject.config.checklist)) { 

        return dataLayerObject.config.checklist;
    }
    return null;
}

var dataLayerObject = {
    config: {
         checklist: ['array of stuff']
    }
}

function isObject(o) {
  return typeof o == 'object' && o !== null;
}

function getChecklistUrl() {
  if (isObject(dataLayerObject) &&
    isObject(dataLayerObject.config) &&
    isObject(dataLayerObject.config.checklist)) {

    return dataLayerObject.config.checklist;
  }
  return null;
}

console.log(getChecklistUrl()[0]);

This makes the code more organized and easier to read, IMHO.

You can also do something like a getter for your objects that take a dot-separated string and return you the property or null if the property doesn't exist:

function getObjProperty(obj, propString) {
    if(!isObject(obj) || !propString || typeof propString != 'string') {
        return null;                                 // make sure obj is an object and propString is a non-empty string
    } 

    var props = propString.split('.');
    for (var i = 0, l = props.length; i < l; i++) {
        var p = props[i];
        if(!isObject(obj[p])) { return null; }       // if current property isn't an object, return null
        obj = obj[p];                                // otherwise update the object to the next one down the property chain
    }
    return obj;
}

You would use it like: getObjProperty(dataLayerObject, 'config.checklist');

var dataLayerObject = {
  config: {
    checklist: ['array of stuff']
  }
};

function isObject(o) {
  return typeof o == 'object' && o !== null;
}

function getObjProperty(obj, propString) {
  if (!isObject(obj) || !propString || typeof propString != 'string') {
    return null;
  }

  var props = propString.split('.');
  for (var i = 0, l = props.length; i < l; i++) {
    var p = props[i];
    if (!isObject(obj[p])) {  // 
      return null;
    } // if current property isn't an object, return null
    obj = obj[p]; // otherwise update the object to the next one down the property chain
  }
  return obj;
}

console.log(getObjProperty(dataLayerObject, 'config.checklist'));

Or you can achieve this with a fairly straightforward recursive method

NOTE:

The examples above check the prototype chain as well. If you don't want this, you should use hasOwnProperty when checking if a property is an object to also check that the property exists on the testing object.

Prefix's answer shows one variation of such an approach.

2 Comments

This is very similar to what I had going in my code to begin with. The question was more around the checking component which is where the original title came from. I really appreciate your answer and effort.
No problem mate, glad to help :)
0

You could do something like this if you want it to be short, but it doesn't check if the dataLayer is an object, so if it's a function with a config and checklist, it'll also return it. But since it's a custom object, it might not be an issue. You can add the 'in' or 'hasOwnProperty' checks if needed, or use a ternary if you prefer it.

var getChecklistUrl = function getChecklistUrl() {
    return dataLayerObject && dataLayerObject.config && dataLayerObject.config.checklist || null;
}

Comments

0

You can always shorten it up by taking advantage of casting:

if ( typeof dataLayerObject == 'object' &&
     dataLayerObject.config &&
     dataLayerObject.config.checklist &&
     typeof dataLayerObject.config.checklist == 'object') {
  // do something
}

If you don't really need to check that checklist is actually an object, you can get rid of the last typeof. The first typeof is required since your variable looks like it might be global.

1 Comment

This will not work in all cases because typeof null == 'object'.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.