JavaScript: Avoiding Repeated Work

By  on  

I love to scavenge the source code of the web's large sites, looking for patterns to both apply to my coding and to find hacks or techniques I'd not heard of before.  One problem I often find with the coding of large sites is repeated operations.  There are a few different mistakes I see when looking at their code and I want to share those with you so you can speed up your own JavaScript code.

Repeated Element Collection

The most problem I see most often is JavaScript code is repeated element collection.  Selector engines and querySelectorAll have gotten to be very fast but repeated work is always a slower than doing the work once.  Obviously the problem and solution look like:

// :(
$$(".items").addClass("hide");
	// ... and later ...
$$(".items").removeClass("hide");


//  :)
var items = $$(".items");
	// ... and use to heart's content from here on out!

Scolding developers for repeated element collection is a daily occurrence but this scolding needs to be reinforced.  Of course repeated element collection cannot always be avoided (think sites with AJAX page loads), but in those cases, you will most likely want to use event delegation instead of direct element retrieval and event application.

Repeated Conditionals

Repeated condition calculation is a common case but also a common pattern which can be avoided.  You will see something like this:

var performMiracle = function() {
	// If the browser has feature A, use it...
	if(features.someFeature) {

	}
	// ... if not, do another
	else {

	}
};

It works but it's not the most efficient use of code, and the conditional checks are run upon each call.  Instead, something like this would be better:

var performMiracle = features.someFeature ? function() {
	// Plan A stuff
} : function() {
	// Plan B stuff
};

Only one conditional and the method or variable is already set to the result of the conditional!

Repeated Object Creation

On repeated operation that goes under the radar is repeated object creation, usually in the form of a regular expression.  Consider the following:

function cleanText(dirty) {
	// Get rid of SCRIPT tags
	clean = dirty.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, "");

	// Do some more cleaning, maybe whitespace, etc.

	return clean;
}

The code above repeatedly creates a new (albeit the same) regular expression object -- an opportunity to save numerous object creations by creating the RegExp in a scope context than the function:

var scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/gi;
function cleanText(dirty) {
	// Get rid of SCRIPT tags
	clean = dirty.replace(scriptRegex, "");

	// Do some more cleaning, maybe whitespace, etc.

	return clean;
}

In the case above, the RegExp is only created once but used many times -- a nice save in processing.

Those are just a few of the issues I see often repeated when I browse JavaScript written by other developers. What other common mistakes do you see?

Recent Features

  • By
    Vibration API

    Many of the new APIs provided to us by browser vendors are more targeted toward the mobile user than the desktop user.  One of those simple APIs the Vibration API.  The Vibration API allows developers to direct the device, using JavaScript, to vibrate in...

  • By
    Create a CSS Flipping Animation

    CSS animations are a lot of fun; the beauty of them is that through many simple properties, you can create anything from an elegant fade in to a WTF-Pixar-would-be-proud effect. One CSS effect somewhere in between is the CSS flip effect, whereby there's...

Incredible Demos

  • By
    Reverse Element Order with CSS Flexbox

    CSS is becoming more and more powerful these days, almost to the point where the order of HTML elements output to the page no longer matters from a display standpoint -- CSS lets you do so much that almost any layout, large or small, is possible.  Semantics...

  • By
    jQuery UI DatePicker:  Disable Specified Days

    One project I'm currently working on requires jQuery. The project also features a datepicker for requesting a visit to their location. jQuery UI's DatePicker plugin was the natural choice and it does a really nice job. One challenge I encountered was the...

Discussion

  1. Great tips David, Love your tutorials =)

  2. Jonathan Kingston

    It is a trade off with readability.

    I would only argue for using the regular expression version where code performance is important or where a different regular expression needs to be passed into the function at different times.

    In terms of DOM reuse that makes complete sense as you can make it more readable by naming what the selection is and also DOM selection is usually far more costly, having a reference to 1000 elements you have selected makes sense. Does a reference to a single regular expression?

    The repeated conditionals section actually does different things and again is much harder to read if those methods were long. Upon execution of those lines the performMiracle in the second example will always be whatever the conditionals parse to on first run, where as: performMiracle(), could mean different things several times depending on context. I have seen many bugs caused by conditional functions like this when race conditions happen that are then hard to fix without rewriting large portions of the code.

    Personally I would always take readability first, then flexibility and then performance. Only caching large searches through objects is something I do while writing my code for the first time.

    • It is a tradeoff with readability, but readability is felt by a few number of devs while speed/perf is felt by *every user*, thus I put the user first.

  3. Jebin

    Nice one @David.

  4. MaxArt

    I tend to avoid conditionals in iterations too, if they don’t depend of the iteration index or local variables. Instead of
    for (…) {
    if (…) …
    else …
    }
    I do
    if (…)
    for (…) …
    else
    for (…) …

    • MaxArt

      (By the way, what’s the point of wrapping code in pre tags if the result is still the same?)

  5. Nick

    Is the cost for creating short strings and checking features really noticeable? If it sacrifices readability, could that not fall under the case of premature optimization? Lower readability = more bugs and slower development. If the cost is negligible might be better to decide to focus on readability anf save time that can be spent on testing and optimzing the bigger performance problems

  6. I generally consider it good practice to have a cache object in any given module for storing selectors and strings. If you name the cached objects well, it actually becomes more readable. An example:

    var myModule = function () {
        var cache = {
            $menuButton: $(#menuButton),
            cleanerEx: "/]*>([\s\S]*?)/gi"
        };
    
        return {
    
            cleanText: function (dirty) {
    
                // Get rid of SCRIPT tags
                clean = dirty.replace(cache.cleanerEx, "");
    
                // Do some more cleaning, maybe whitespace, etc.
    
                return clean;
    
            }
        }
    };
    
  7. Luis Deleon

    It should be stressed more that Repeated Conditionals case is only ussable when features.someFeature doesn’t change, I know it is implied, I also know there is people who will not hesitate to use it on normal variables jeje

  8. Andrew

    > It is a tradeoff with readability, but readability is felt by a few number of devs while speed/perf is felt by *every user*, thus I put the user first.

    This sounds a bit dogmatic, doesn’t it? If your performance enhancement is too little to get noticed by anyone, the only persons who will feel more unreadable code are the devs (if they don’t introduce some bugs because the code isn’t readable). However, you also have the problem that you have to introduce more variables which are exposed to not related parts of your software. And if you have many regex functions, you also need to invent some clumsy variable names for each function’s regex expression which can get very unhandy.

    Nevertheless, you can use nested functions (currying) or the bind method to bind your variables to fix objects:

    var cleanText = function(regex) {
    					return function(dirty) {
    						clean = dirty.replace(regex, "");
    						return clean;
    					}
    			 }(/]*>([\s\S]*?)/gi);
    
    var cleanText = function(regex, dirty) {
    				clean = dirty.replace(regex, "");
    				return clean;
    			  }.bind(this, /]*>([\s\S]*?)/gi);
    

    By the way: Regex expressions are compiled the first time they are evaluated and therefore the object creation time is almost negligible.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!