Writing Compressible JavaScript

Writing a library is a balancing act between the (sometimes competing) interests of API clarity, code clarity, performance and compressibility. In this article I’m going to detail three of the approaches we take to meet this balance and suggest them for your own usage.

1. Collecting var statements

Every time we declare variables we add 4 bytes to the compressed file size. Variables are declared sufficiently often that this can really add up, so instead of this:

var myFirstVar = 'something'; 
var myOtherVar = 'another thing'; 
var answer = 42; 
var adama = true;

One should use this form:

var myFirstVar = 'something', 
    myOtherVar = 'another thing', 
    answer = 42, 
    adama = true;

When this code is compressed, each variable name above is turned into a single-letter name, meaning that the wasted 4 bytes per useless additional ‘var ‘ in the first example would have contributed significantly to code size with no benefit.

2. Local variable pointers to object properties

The following code (pruned from a previous version of Ext JS) is not as compressible as it could be:

var cs = {}; 
for (var n in this.modified) { 
    if (this.modified.hasOwnProperty(n)) { 
        cs[n] = this.data[n]; 
    } 
} 
return cs;  

We’re better off aliasing ‘this.modified’ to a local variable first. Aside from the performance benefits some JS engines derive from not having to perform object property lookups over and over again, we save precious bytes this way too:

var modified = this.modified, 
    changes  = {}, 
    field; 

for (field in modified) { 
    if (modified.hasOwnProperty(field)) { 
        changes[field] = this.data[field]; 
    } 
}

return changes;

Again, the minifier will compress those variable names down to a single character each, so for our ‘this.modified’ example we’re going to use 15 bytes to define the variable plus 1 byte each time we use it (totalling 15), vs the 26 bytes for that code previously. This approach scales especially well – were we to refer to this.modified a third time in the function, as our code will now minify to 16 bytes, vs 39 without the variable declaration.

Side note: in the first example here the variable ‘n’ was being used in the for…in loop. We can always safely exchange that for a meaningful variable name (in this case ‘field’) and leave the rest to the minifier.

3. Aliasing ‘this’ to ‘me’

In Ext.data.Record’s markDirty method we have the following code:

this.dirty = true; 
if(!this.modified){ 
    this.modified = {}; 
} 
this.fields.each(function(f) { 
    this.modified[f.name] = this.data[f.name]; 
},this);

There are 7 references to ‘this’ in that method, taking 28 bytes and completely incompressible. We could rewrite it like this (note that the final ‘this’ can be removed in this format):

var me = this; 

me.dirty = true; 
if (!me.modified) { 
    me.modified = {}; 
} 
me.fields.each(function(f) { 
    me.modified[f.name] = me.data[f.name]; 
});

Again, our minifier will change the ‘me’ var to a single character, saving us 8 bytes in this instance. That might not sound like a lot but after minification it equates to a 7% reduction in code size for this function.

In each of the cases above we’re generally talking about single-digit percentage savings after minification. There is value in that small slice though, especially as more and more applications shift onto bandwidth-constrained mobile platforms.

The first two approaches are no-brainers and must always be done but the third is slightly more controversial. Personally I find it makes the code a little harder to read, largely because my syntax highlighter doesn’t recognise that ‘me’ is now the same as ‘this’. Its value also varies significantly by function – some functions can contain over a dozen references to ‘this’, in which case this approach makes a big difference.

13 Responses to Writing Compressible JavaScript

  1. Lloyd K says:

    Hi Ed!

    Nice article as always, however surely since most web servers now supply said JavaScript files compressed under gzip or deflate such savings are marginal at best?

  2. Arthur Kay says:

    Awesome post Ed! I never really gave much thought to compressing var statements but your analysis is spot-on. Thank you for posting this!

  3. Ed Spencer says:

    @Lloyd that’s definitely true in most cases, but applying these techniques provides a good fallback for when gzip is not enabled (which does happen surprisingly often). For the most part it wouldn’t be worth going through the entire library and updating it with this techniques but it makes sense to do so for new or refactored code.

    @Arthur thanks, I’ll try to remember to blog more often now that Sencha Touch is public

  4. Rene Saarsoo says:

    The collecting of var statements is clearly something that the compression tool should do for you. Even if the compression tool doesn’t support it by itself, it’s relatively easy to write a preprocessor that converts your var statements from one form to another before passing on to the compression tool.

    The other two approaches could IMHO also be automated, at least to some extent. Although as easily as with the var statements.

  5. Rene Saarsoo says:

    Wanted to write: Although NOT as easily as with the var statements.

  6. Pingback: Tweets that mention Writing Compressible JavaScript : Ed Spencer -- Topsy.com

  7. F_D says:

    Collecting the var statements is a path potentially fraught with peril if you are not careful and disciplined (also: …if your collaborators are not also careful and disciplined). Miss one comma and you’ve accidentally moved a sub-set of your variables into the global scope. (A potentially nightmarish debugging session, for sure–especially in a large codebase.) Not that there are not benefits to this pattern (it is one that I use myself)–but also a good idea to consider the risks.

  8. Brent says:

    Super killer advice, thanks !!

  9. Ryan says:

    Very interesting blog post. I’m almost certain you are correct – some of these techniques will help reduce the size of the final application JavaScript by a measurable amount. However, I think we need to be careful not to sacrifice the readability of our code for the sake of the final file size. It is quite easy to see these techniques getting out of hand and ending up with code that is very difficult to follow. As we all know, this problem will remain hidden in the short term but will manifest itself once subsequent engineers try to work with the code. In the end, the amount of time wasted trying to understand the code by the new engineers probably won’t be worth the savings in file size and might even be better directed towards improving your compression tool to do some of these things for you. In particular, example 1 and 3 above look like prime candidates for automation.

  10. Ed Spencer says:

    @Ryan agreed – readability is paramount. Point 3 is really the killer that I wouldn’t want to do too often, it’s far better left to a minifier

  11. Hi Ed

    I’m wondering why you choose to substitute “this” with the two letter word “me”, and not something even shorter like “m”?

    I noticed the extensive use of the me-pattern in the new ExtJS4. Once you sacrifice the readability, then why don’t go all the way? πŸ™‚

    Sebastian

  12. Ed Spencer says:

    @Sebastian it doesn’t actually matter which variable you alias ‘this’ to as your minifier (YUI compressor or similar) will minify those anyway. So var someAliasToThis = this; is as good as var me = this;

    Ideally the minifier would the var me = this substitution automatically when appropriate. If we can get that working reliably we’ll move to that pattern.

  13. Latasha says:

    q0NWAI Good point. I hadn’t tghohut about it quite that way. πŸ™‚

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: