(More) Assignment in Conditions

Tweet

This is a follow-up to the post I wrote a few months ago, Assignment inside a Condition, in which I described a technique for assigning and evaluating variables in the same expression, and demonstrated it’s use for retrieving and referencing DOM nodes.

Though quite a few of you felt that the syntax was too confusing to be worth using; that it just makes for code which is too difficult to read:

if(summary = document.getElementById("post-summary"))
{
    return summary.innerHTML;
}

I have a certain sympathy with that point of view; but not very much, because the end-result is expressions that are less code, usually faster*, and can save memory (the top three goals of optimization!)

Carry on regardless

So over the last few months I’ve been experimenting with this technique, in a fairly ad-hoc way, looking for situations where it might be applicable and useful. I came up with a couple of new use-cases that seemed to work quite nicely; but I also kept seeing a problem that ultimately couldn’t be ignored (which one previous reader did in fact point-out) — the problem is one of variable scope and the undue creation of globals.

Avoiding the creation of globals

In the code example above, the expression has created a variable called summary; but the variable declaration is not preceded by var, nor could it be in that context:

if(var summary = document.getElementById("post-summary"))

That would just throw an error over the invalid syntax.

But without the preceding var we’ve created a global variable, because of JavaScript’s implied globals behavior (ie. any variable that’s not explicitly declared with var is created as a global). If we’ve already declared the variable elsewhere, then it’s not a problem. Otherwise, we’ll have to declare it first; but if we have to do that separately, before the assignment condition, then we’ve undermined some of the shortcut-value of the syntax!

So what I did in the end was simply declare a bunch of variables in advance, which can then be used anywhere, safe in the knowledge that their scope is constrained. It doesn’t really matter where you do it, as long as they’re encapsulated and available where you’ll need them — if you’re working inside a class or module pattern, then the top of its closure would seem like an obvious place. I ended up using the same variable names over and over again, wherever I needed temporary vars in order to use this syntax; and by re-using them, rather than creating a new one each time, I was ultimately able to save more memory.

Brackets enclosing assignment

As we noted last time, assignment returns a value, and if the assignment is wrapped in parentheses, then it’s enclosed as a single statement. It’s evaluated first, like in mathematical equations, and so we can use it exactly as though it contained the literal value.

This gives us new possibilities — to assign a value, and then perform an arbitrary evaluation on that value:

if((foo = bar) > 0)
{
    //foo is greater than zero
}

The variable bar in that example could equally be the return from a function, and saving it allows us to work with that value again, inside the condition:

if((foo = getFooValue()) > 0)
{
    alert(foo + " is greater than zero");
}

This is particularly useful as a way of saving shortcuts to long property references, which ultimately make the code run faster, as less object-traversal is necessary:

if((foo = this.prop.subprop.childprop.value) > 0)
{
    alert(foo + " is greater than zero");
}

Saving the execution of a regular-expression

Using this syntax, we can — execute a search for a regex pattern within a string, save the array of matches, and evaluate the result — all in the same expression (nb. exec is a method of RegExp, and returns either the matches array, or null if there were no matches):

if(matches = /^([a-z]+)[:](.*)$/.exec(data)) 
{ 
    alert("The key is " + matches[1] + " and the value is " + matches[2]); 
}

In that example, we’re relying on the fact that null evaluates to false and the matches array to true, and that’s a safe assumption in this case. But all the same, we don’t have to do that — we could wrap all of that in parentheses and then do a strict evaluation:

if((matches = /^([a-z]+)[:](.*)$/.exec(data)) !== null) 
{ 
    alert("There are " + matches.length + " matches"); 
}
else
{
    alert("There are no matches");
}

Carry on — don’t lose your head!

I’ll be the first to admit that this syntax looks a little odd, and thereby carries the potential for confusion and misunderstanding. But then, that’s true for countless things (at first) and not a reason for discarding them; like anything else — with time, it becomes familiar. And what it has to offer, to my mind, far outweighs what it costs:

  • What it offers is optimization — a tangible concern that affects all our users
  • What it costs is some readability — an abstract concern that only affects us

I don’t know about you, but I’d call that a bargain!

Thumbnail credit: sbwoodside

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Bob

    Like you said, the syntax is a little odd. Honestly, the benefits don’t outweigh the initial loss in readability for me. If you’re only going to be doing this in function scope, then you don’t really gain in saving memory since the garbage collector should clean them up after you leave the function, where a global variable occupies that memory for the lifetime of the program.

    Additionally, I think the speed gains will be very negligible in modern browsers.

  • Mike

    A couple of points.

    Firstly, beware of premature optimization. It’s one of the oldest adages of software development. If a system is performing badly, then you optimize. The first place you usually look is the database BTW not Javascript. If someone’s doing something evil to the database that will kill performance worse than just about anything else you can do.

    Secondly, has the benefit been quantified? Write two nearly pages that do the same thing (a lot) using each method & time the performance. If you can find a way to profile the code to isolate the effect of your optimization that’s better. If it’s only giving you a few percent and the support cost is going to be much higher, just say no. Remember, support costs are typically >70% of the TCO of the system. So if you make it harder to support the TCO gets multiplied.

    The technique’s pretty cool but when you have to support it in 12 months time you could end up cursing yourself – even worse if a less experienced coder is trying to figure out what the heck is going on.

    One of the original “C” guys (Kernighan or Ritchie – I forget) said something like “Debugging is much harder than writing code. So if you write code at the limit of your comprehension the you will not be smart enough to debug it”

  • Ian Sweeney

    “The benefits don’t outweigh the initial loss in readability for me”.

    Me neither. I think the loss of readability is a very bad thing. I think it’s a poor coding practice and would only be justified if major performance gains could be achieved. I can;’t see how this would ever be the case.

  • Steve

    I agree with the commenters so far. You shouldn’t give up readability for an unquantified performance benefit.

    I would even argue about the performance benefit. Given a compiler (either on-the-fly or old-school static), the following two are likely to compile down to the same result:

    var x;
    if ( x = y ) {

    and

    var x;
    x = y;
    if ( x ) {

    I will grant you that the second form has more characters to parse, so, yes, the first form is likely faster. But not by enough.

    • Aankhen

      +1. Frankly, given that you have to declare the variables in advance since var can’t be used within an expression, this is a fairly silly technique to use in JavaScript.

  • kira8080

    Readability — an abstract concern that only affects us ??

    When the code gets so unreadable that it becomes impossible to add new features, don’t you think it affect users too ? Of course it does, and much more than a few microseconds less at page load.

    I’d say go for readability first. If using an assignment inside a condition makes the code cleaner (sometimes it does), only then go for it. Otherwise don’t use it.

  • Taylor

    Many good points made so far. Another one to add:

    This code
    if((foo = bar) > 0)
    may work okay in javascript, but the behavior is incredibly language-specific and can’t be relied on (and certainly not ported). The result of an assignment isn’t always the value being assigned and to find out you need to dig deep into the language’s specs. In fact, is this part of the Javascript spec? If not, you might find some browsers make that if always true or always false.

    For the past 10 years or so there has been a clear movement in programming away from a performance focus to a clear code focus. Object Oriented programming is a clear example since objects almost always add a slight overhead, but the clarity/easiness of coding makes up for it. I think you’re fighting a losing battle.

    • Taylor

      On second thought, it must be in the spec since you can do
      var x = y = z= 2;

      but it still looks more like a bug than a not-bug

  • kaf

    Holy crap I would hate to be the poor sucker who has to maintain your code!

    This technique to me is more of a hack that may be used for very specific performance issues. Similar to how graphics programmers sometimes have chunks of Assembler in their C code.

    Most devs should NEVER need to use it. But if it is used, it must be heavily commented with a warning that a performance hack is present.

    • Aankhen

      There’s nothing hacky or buggy about it, and its purpose is not improved performance. It’s simple shorthand that takes advantage of the fact that assignments are expressions.

      • http://www.brothercake.com/ James Edwards

        Exactly!

        It’s not a performance hack, it’s not an optimization, it’s just a coding form. I mentioned its performance advantages because they exist, but they’re not the real point. The real point is that is that it’s a convenient shorthand format.

        It is supported by the specification of the language (assignment returns the value being assigned), and it’s really not difficult to use. I’ve paid lip service to that idea to try and be open minded, but really — there’s nothing tricky or confusing about this.

        But if you think there is, fine, don’t use it.