Anyone who’s dealt with a deeply nested set of properties in JavaScript, whether through use of an extensive third-party JavaScript API or a custom library, has likely run into the problem of safely accessing such structures. One can of course hand-roll many ugly, hard-to-read statements like the following, adding on to the maintenance burden of the code (splitting lines as necessary for length, of course):
if (a && a.b && a.b.c && a.b.c.d && a.b.c.d.e) { doSomethingWith(a.b.c.d.e); }
The main alternative to this approach is to make such accesses within a try
-catch
block, but this is generally slower by at least a couple of orders of magnitude when exceptions are thrown, so not always useful in tight loops and other performance-sensitive situations. It’s also arguably an abuse of the try
/catch
mechanism.
Luckily, a less unsavory solution with fairly good performance can be adopted using JavaScript prototype methods. Here’s a reference implementation, and you can also try it for yourself (with timings):
Object.prototype.hasSubproperty = function() {
if (arguments.length == 0 || typeof(arguments[0]) != 'string') return false;
var properties = arguments[0].indexOf('.') > -1 ? arguments[0].split('.') : arguments;
var current = this;
for(var x = 0; x -1 ? arguments[0].split('.') : arguments;
var current = this;
for(var x = 0; x < properties.length; x++) {
current = current[properties[x]];
if ((typeof current) == 'undefined') return undefined;
}
return current;
};
Object.prototype.getSubproperty = function() {
if (arguments.length == 0 || typeof(arguments[0]) != 'string') return false;
var properties = arguments[0].indexOf('.') > -1 ? arguments[0].split('.') : arguments;
var current = this;
for(var x = 0; x < properties.length; x++) {
current = current[properties[x]];
if ((typeof current) == 'undefined') return undefined;
}
return current;
};
Object.prototype.setSubproperty = function() {
if (arguments.length -1 ? arguments[0].split('.') :
Array.prototype.slice.call(arguments, 0, arguments.length - 1);
var parent, current = this;
for(var x = 0; x < properties.length - 1; x++) {
current = current[properties[x]];
if ((typeof current) == 'undefined') return false;
}
current[properties[properties.length - 1]] = arguments[arguments.length - 1];
return true;
};
Some observations: If you run the timings, you’ll note that the try
-catch
method is still quite fast when exceptions are not thrown, indicating that try
-catch
might be workable when exceptions are expected to be truly… exceptional. Still, in any but extraordinary conditions, the performance of the prototype-method approach should be quite fast enough, avoids worst-case performance, and is cleanest overall.