Writing Compressible JavaScript
June 19, 2010 by Ed Spencer · 8 Comments
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.
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?
Awesome post Ed! I never really gave much thought to compressing var statements but your analysis is spot-on. Thank you for posting this!
@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
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.
Wanted to write: Although NOT as easily as with the var statements.
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.
Super killer advice, thanks !!