O'Reilly

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?

Track.js Error Reporting

Recent Features

  • 9 Mind-Blowing WebGL Demos

    As much as developers now loathe Flash, we're still playing a bit of catch up to natively duplicate the animation capabilities that Adobe's old technology provided us.  Of course we have canvas, an awesome technology, one which I highlighted 9 mind-blowing demos.  Another technology available...

  • 7 Essential JavaScript Functions

    I remember the early days of JavaScript where you needed a simple function for just about everything because the browser vendors implemented features differently, and not just edge features, basic features, like addEventListener and attachEvent.  Times have changed but there are still a few functions each developer should...

Incredible Demos

  • Unicode CSS Classes

    CSS class name structure and consistency is really important; some developers camelcase classnames, others use dashes, and others use underscores.  One thing I've learned when toying around by HTML and CSS class names is that you can actually use unicode symbols and icons as classnames....

  • Do / Undo Functionality with MooTools

    We all know that do/undo functionality is a God send for word processing apps. I've used those terms so often that I think of JavaScript actions in terms of "do" an "undo." I've put together a proof of concept Do/Undo class with MooTools. The MooTools...

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!

Recently on David Walsh Blog

  • Serve a Directory via Python

    Sometimes I'm working with a test HTML file and some JavaScript but need to work off of a served space.  In that case, I sometimes need to swap out folders within MAMP Stack which leads to a maintenance nightmare.  Bleh. I recently found out that you can...

  • OSCON Portland:  Conference  Discount!

    O'Reilly puts on the best web industry conferences in the world.  These conferences include Fluent Conference, Velocity Conference, and the upcoming OSCON in Portland, Oregon from July 20-24.  Open Source Convention (OSCON) is a conference that focuses specifically on open source developers and the tools and possibilities...

  • Follow Redirects with cURL

    I love playing around with cURL. There's something about loading websites via command line that makes me feel like some type of smug hacker, just like tweeting from command line does. I recently cURL'd the Google homepage and saw the following: I found it weird that Google...

  • Developers Have WordPress, Amateurs Have Squarespace, Professional Designers Have the NEW Webydo!

    Web design platforms have traditionally come in one of two varieties. There are the solutions like WordPress and Drupal that are incredibly powerful, but an understanding of web development and coding is required to be able to use those platforms effectively. On the other side of the...

  • Chris Coyierâs Favorite CodePen Demos II

    Hey everyone! Before we get started, I just want to say it’s damn hard to pick this few favorites on CodePen. Not because, as a co-founder of CodePen, I feel like a dad picking which kid he likes best (RUDE). But because there is just so...