Friday, July 11th, 2008
A safety fence for your property lookups
Michal Till posted a little JavaScript tip that he uses to create a safety fence for accessing properties:
As we all know, null not only does not have any properties, but their existence also can not be tested. So null.property retuns error instead of undefined.
You can end up with something like this:
JAVASCRIPT:
if ( (node) && (node.nextSibling) && (node.nextSibling.className == ...) ... ) { ...There is a neat trick that can simplify the boolean expression. We might supply an empty object {} or zero (0) as an alternative:
JAVASCRIPT:
if ( next = (node || 0).nextSibling) ) {












Nice, this turns the if clause which clearly expresses that the developer expects node or node.nextSibling to possibly be null into an clever/unreadable hack! If only more people properly abused language constructs this way..
if (((node || 0).nextSibling || 0).className == 'hello')if (node && node.nextSibling && node.nextSibling.className == 'hello')The ‘neat trick’ isn’t that much shorter and a lot less readable! Must agree with bosmeeuw
It is shorter, and I don’t find it unreadable at all. I know right away when I see it that the programmer is trying to guard against a missing attribute.
.
But I don’t think I’d do it (for me, the less parens the better).
.
I do like to see these things, though. Good for the brain.
Also, sometimes these tricks are useful for people writing compilers that target JS as an assembly language. Shorter is usually better in those cases. You might generate the more typical code for debugging and the tighter code for production.
.
Another problem, though, is that the && will shortcut out, so it’s probably faster than the || solution.
Well, unless speed was of the utmost importance, as in a game, wouldn’t this simply be a case where a try/catch block would be much clearer:
try{
if (node.nextSibling.className == "something")
// do something
}
catch(e) {
// catch error
}
@bosmeeuw, philm:
I think if, upon reading an explanation of what it does, you still find it unreadable, you’re probably doing something wrong.
.
@Nosredna:
I am on the fence; I think the “syntactic sugar” might eventually prove beneficial enough that the parens could become reflex.
.
As for || vs &&, first of all, if that’s your bottleneck, you need to rethink your approach. Second of all, just make sure you use the optimal condition order for your use case.
I use This get function:
get=function(hash,arr){
for(var i=0,l=arr.length;i<l;)
if(!hash || (hash=hash[arr[i++]])===undefined )return;
return hash;
}
//And
if(get(node,['nextSiÂbling', 'clasÂsName'])){
...
}
This has been mentioned before with Oliver Steele’s Maybe Monads on the cheap where he recommends the following maybe monad null propagation behaviour:
(object||{}).property
http://osteele.com/archives/2007/12/cheap-monads
Thanks your fenceï¼
compare :
var r; if(node&&node.nextSibling&&(r=node.nextSibling.className)=='x') return r;
var r; if((r=((node||0).nextSibling||0).className)=='x') return r;
var r; if(r=node)if(r=r.nextSibling)if((r=r.className)=='x') return r;
Using a simple if is maybe a bit longer but faster :-)
heh, so many different ways of accomplishing the same thing :)
@linb: I like your function. One could use a variadic function instead of passing a second parameter as an array for the extra sugar punch (i.e. so the function call looks like get(node, “nextSibling”, “className”) ), though I suspect some people might object with hypothetical performance issues with the body of the function itself.
@RichB
(object||{}).property is less efficient due to the fact than an anonymous object needs to be created – in a tight loop of hundreds of iterations it will be noticeable.