Variable Coalescing

I wanted to take a moment to un-obfuscate a technique of mine for others so I can continue to use it in my code.

I used a technique in my code the other day to work around a browser issue.  A colleague informed me during code review that the technique wasn’t immediately obvious and possible violated our “no clever code” standard.  We kept it in for other specific reasons, but I wanted to take a moment to un-obfuscate the technique for others.

The Issue

I’ve actually used this technique in two places: JavaScript and PHP.

In JavaScript, there’s a bug in Internet Explorer 9 that will crash your script if any calls are made to [cci]window.console[/cci] while the developer tools are closed. Other browsers ignore the call (including other versions of IE), but version 9 will die gloriously.

The easiest fix is to, of course, not ship debugging calls in production code.

In situations where you absolutely need to leave this debugging code in place, however, it’s often useful to create your own version of the console logger if it doesn’t exist. Within a closure, I’ll sometimes place code like the following:

[cc lang=”js”]var console = window.console || { log: function() {} };[/cc]

Inside the closure, this defines a local console object that will either default to the global one (if available) or a custom short-circuit. We’re using the falsey nature of [cci]undefined[/cci] here to provide the fallback, but this is not what I mean by variable coalescing.

JavaScript

The above approach works well for single scripts. If your application has multiple scripts (ideally inside separate closures), it becomes redundant as you need to repeat the same code inside every closure.

It’s an easy line of code to forget; particularly if you’re used to following the “don’t ship debugging statements” rule. Instead of building a short circuit, it’s often better to just check for the existence of the console object before using it.

if ( undefined !== window.console ) {
window.console.log( 'some error.' )
}

This works, but turns every debugging line into 3.[ref]Yes, you could put the entire conditional and brackets on one line, but that also violates coding standards and many formatting tools will automatically re-align things anyway.[/ref] The situation that warranted this change I mentioned above was in a vendor script, and I didn’t want to change the line numbering in the file. My change instead was to use:

window.console && window.console.log( 'some error.' );

Again, we’re using the falsey nature of [cci]undefined[/cci] to our advantage here. If the console object doesn’t exist, the [cci]&&[/cci] operation will prevent execution of the following statement. If the object is available, then the method call will fire as expected.

PHP

When I build tasks for WordPress’ CLI system, I use a similar approach. I usually create a [cci]–verbose[/cci] flag for my scripts that prints more detailed messages to the interface. Inside my script, this means I have to wrap calls to [cci]WP_CLI::line[/cci] in checks for whether or not the flag is set.

Using variable coalescing, my more verbose calls become:

$verbose && WP_CLI::line( 'Some message.' );

When I add a progress bar to my scripts, I usually store it inside a variable called [cci]$notify[/cci]. Like the verbosity flag above, I can use variable coalescing to fire the progress bar only in situations where it actually exists:

$notify && $notify->tick();

Clever Code

Does this qualify as a clever trick? If I were the one who’d come up with it, I’d probably say “yes” just to appease my ego. I didn’t invent the technique, though, and from an outside perspective I don’t see it as being more clever than things like closures returning functions or type coercion in cache lookups.

It is, however, an underused technique that many could stumble over the first time they see it. That’s partly the reason I want to document both the technique and a couple of use cases here.

The more developers using a technique, the better the documentation around it becomes, and the easier it is to justify its use in your own code.