4 JavaScript Minifiers Compared

 

Professional Website PerformanceMinification is the act of stripping out unnecessary characters from code to reduce the size, and a minifier is the tool that does it. Most often, the term is applied to JavaScript although the technique can also be used on CSS and (to some extent) HTML.

For the web master, the aim of minification is, of course, to reduce file size and thus speed up transfer times for clients. Using gzip compression offers bigger reductions in file size, and it’s often claimed that this makes minification redundant — a minified, gzipped page isn’t much smaller than an unminified, gzipped page. Although there is some truth in this argument, minification is still a useful technique. And with approximately 10 percent of web traffic passing through browsers that don’t support gzip compression (see http://developer.yahoo.com/performance/rules.html), there is a sizable minority of users for whom minification can help a lot.

The downside with minification is that code becomes difficult to read and modify. One solution is to store your code unminified and then minify it on-the-fly, as clients request it. This is a terribly inefficient way to handle what are usually fairly static resources, though, and definitely not something recommended. Instead, you should keep an unminified copy of the code in a separate directory and pass it through the minifier when you are ready for it to go live, possibly as part of your build process.


NOTE For all but the most basic websites, it is common to automate the process of deploying a new version of the site via a basic shell script or batch file. This might consist of copying the code over from a development/staging area, setting the correct file permissions, pre-compressing resources, and so on. This is an ideal place in which to perform minification.

What are “unnecessary characters”? In the languages discussed here, whitespace and comments are the main candidates for removal, and many simplistic minifiers remove only these. This is often just scraping the surface of what is possible though — especially with JavaScript.


NOTE Be aware that minification shouldn’t be used as an excuse to write sloppy code. Don’t fall into the trap to think that it’s okay to write bloated, badly structured code because a minifier will clean it up for you. Your first step should always be to manually clean up your code as much as possible; only then should you pass it through the minifier.

JAVASCRIPT MINIFICATION

JavaScript offers great potential for minification. Aside from removing whitespaces and comments, Windows-style line breaks (CRLF) can be converted to UNIX-style breaks (LF), and variable names can be shortened.

Let’s look at the typical minification process for a small block of code. In this case, the code handles a function to toggle the “visibility” (more accurately the display) of an element:

function toggle(elementID) {

        if ( document.getElementById(elementID).style.display != 'none' ) {
               document.getElementById(elementID).style.display  = 'none';
        }
        else {
                document.getElementById(elementID).style.display = '';
        }
}

As it stands, this function weighs in at 297 bytes.

Before you run it through a minifier, you should attempt some manual optimization. Using a variable holding a reference to the element’s display would be a good start, as shown here:

function toggle(elementID) {
        var el = document.getElementById(elementID);
        if ( el.style.display != 'none' ) {
                el.style.display = 'none';
        }
        else {
                el.style.display = '';
        }
}

This takes the weight down to 246 bytes.

You can simplify things a bit more by getting rid of the if/else block and using the ternary operator:

function toggle(elementID) {
        var el = document.getElementById(elementID);
        ( el.style.display != 'none' ) ? el.style.display = 'none' :
             el.style.display = '';
  }

This takes it down to 193 bytes. So far, you have preserved whitespaces, and the code is still readable.

Now that you’ve seen how to clean up this code, you can pass it through a minifier.

YUI Compressor

One of the most popular minifiers is Yahoo’s YUI Compressor. It’s a command-line minifier, written in Java, that can process both JavaScript and CSS. You can download it from http://developer.yahoo.com/yui/compressor/. (Of course, you must have Java installed.) Running it is simple, as shown here:

$ java -jar /usr/local/bin//yuicompressor-2.3.5/build/yuicompressor-2.3.5.jar
     input.js > output.js

Now try it on the sample function introduced earlier:

$ java -jar /usr/local/bin/yuicompressor-2.3.5/build/yuicompressor-2.3.5.jar
     function.js > function_minified.js
$ cat function_minified.js
function toggle(a){var b=document.getElementById(a);if(b.style.display!="none")
     {b.style.display="none"}else{b.style.display=""}};

A few things have happened here. Unnecessary whitespaces have been removed, and the two variables have had their names shortened: elementID to A, and el to B. Because these variables exist only inside the function, it’s safe to rename them without worrying about it impacting other code.

This takes the function’s size down to 93 bytes — a significant improvement from the original 342.

Just to stress the importance of manually optimizing your code first, look at how the YUI Compressor copes with the original function before you made any changes to it:

function toggle(A){var B=document.getElementById(A);
     if(document.getElementById(A).style.display!="none")
     {document.getElementById(A).style.display="none"}
     else{document.getElementById(A).style.display=""}};

It has still renamed the local variables and removed whitespaces, but the long-winded if/else block with references to the element’s display are still there, and the size is down to only 204 bytes. The YUI Compressor may be clever, but it’s not a mind reader.

You may have wondered why, in the manual optimization examples, var el = document.getElementById(elementID); was not simply written to el = document.getElementById(elementID); to save a few more bytes.

Although it may seem that declaring a variable with var is unnecessary, there is a subtle impact on the variable’s scope. Variables defined inside a function with var are local — that is, they exist only inside the function. Initializing a variable without var causes it to have a global scope, and it is available to code outside the function.

Aside from it making good programming sense to use local variables (because that lessens the potential for variable names to clash), the choice of a local versus global variable also has an impact on minification. For example, say that you run the following through the YUI Compressor:

function toggle(elementID) {
    el = document.getElementById(elementID);
    (el.style.display != 'none' ) ? el.style.display = 'none' : el.style.display = ";

Following is the output this time:

function toggle(A){el=document.getElementById(A).style.display;
     (el!="none")?el="none":el=""};

The YUI Compressor realizes that el is a global variable and shies away touching it in case it breaks another part of your code. In general, it’s best to define your variables with var.

Google Closure

The YUI Compressor is not the only JavaScript minifier out there. The new kid on the block at the moment is Google’s Closure Compiler (http://code.google.com/closure/compiler/), which is set to become the leader of the pack. Crude benchmarking suggests its standard mode offers levels of minification similar to the YUI Compressor, whereas the advanced (and less foolproof) option has the capability to make more savings.

Two of the most exciting features of this advanced mode are function inlining and the removal of unused code.

Inline expansion (or inlining) will be familiar to anyone who has studied C compilers. It’s the act to insert the contents of a function into the place where the function would have been called. This can improve the execution speed (by cutting out the overhead involved in calling the function), and — in the case of small functions that are rarely called — reduce the overall file size.

Let’s look at an example.

function showalert(message) {

        el = document.getElementById('alertbox');
        el.innerHTML = message;
        el.style.visibility = 'visible';

}

x = document.getElementById('formfield');

if (x.value == "no") {
        showalert("Did you really mean no?");
}

NOTE innerHTML is nonstandard but is well supported and a little faster than the alternatives.

This is not a useful piece of code, but it serves nicely as an example. Look at the value of a domain object model (DOM) element named formfield. If the value is "no", you call a function to display an alert message.

Let’s see what happens when you run this through Closure. For the sake of readability, let’s use the PRETTY_PRINT formatting option to preserve whitespaces.

java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS  --js
     example.js --formatting PRETTY_PRINT

x = document.getElementById("formfield");
if(x.value == "no") {
  el = document.getElementById("alertbox");
  el.innerHTML = "Did you really mean no?";
  el.style.visibility = "visible"
}
;

As you can see, the showalert function has been removed. Its content has been placed inline where the showalert() call previously was.

That’s fine in this simplistic example, where the function is called only once. But what happens if your code makes multiple calls to the function, as shown here?

function showalert(message) {

        el = document.getElementById('alertbox');
        el.innerHTML = message;
        el.style.visibility = 'visible';

}

x = document.getElementById('formfield');
if (x.value == "no") {
        showalert("Did you really mean no?");
}

y = document.getElementById('formfield1');
if (y.value == "no") {
        showalert("Did you really mean no?");
}

z = document.getElementById('formfield2');
if (z.value == "no") {
        showalert("Did you really mean no?");
}  

The result is as follows:

java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS
     --js example.js --formatting PRETTY_PRINT

function a(b) {
  el = document.getElementById("alertbox");
  el.innerHTML = b;
  el.style.visibility = "visible"
}
x = document.getElementById("formfield");
x.value == "no" && a("Did you really mean no?");
y = document.getElementById("formfield1");
y.value == "no" && a("Did you really mean no?");
z = document.getElementById("formfield2");
z.value == "no" && a("Did you really mean no?");

This time, Closure had the sense not to inline the function.

The second cool feature of the advanced mode — removal of unused functions — is straightforward. If Closure deems that a function will never be called, it removes it.

The danger emerges when the function is called from elsewhere (for example, if you have a bunch of JavaScript files and are passing them through Closure one by one) or is called by means of an eval() statement. eval statements are a common stumbling block for minifiers, are slow to run, and are a potential security problem. So, whenever possible, you should avoid using them anyway. Google also recommends compiling all the code used on a page in one go (by passing multiple filenames to Closure at the command line) to lessen the problem of functions being removed.

Comparison of JavaScript Minifiers

So far, you’ve learned about the two big players, the YUI Compressor and Google Closure. But there are plenty of other JavaScript minifiers out there.

The examples presented thus far have been small, hence the savings have been small, too. To illustrate how useful minification can be, a bigger example is needed.

Prototype (http://www.prototypejs.org/) is a popular JavaScript framework for AJAX developers. Version 1.6.0 consists of a single JavaScript file, weighing in at 122 KB.

Table 4-1 shows the results of passing prototype.js through five of the most popular JavaScript minifiers.

TABLE 4-1: JavaScript Minifier Comparison

MINIFIER UNCOMPRESSED (KB) GZIPPED (KB)
None 122 28
YUI Compressor 71 21
JSMin 91 23
Closure (simple) 70 21
Closure (advanced) 55 18
Packer (Perl port) 90 23

As you can see, the YUI Compressor and Closure are easily the leaders, and — in the case of the Closure’s simple mode — offer similar levels of compression. If you’re feeling brave, and are willing to debug any errors, Closure’s advanced mode offers an even greater boost.

These results also address the criticism of minification given at the beginning of this chapter — that minification is largely redundant because of gzip compression. The difference in size between the gzipped unminified code and the gzipped YUI Compressor output is 7 KB, a reduction of 25 percent. With Closure’s advanced mode, the decrease in size is more than 30 percent. Coupled with the fact that minified code tends to execute slightly faster, minification of JavaScript certainly should not be ignored.

This article is excerpted from chapter 4 "Keeping the Size Down with Minification" of Wrox's Professional Website Performance: Optimizing the Front-End and Back-End (ISBN: 978-1-1184-8752-5, copyright 2013 John Wiley & Sons) by Peter Smith. Peter G. Smith has been a full-time Linux consultant, web developer, and system administrator, with a particular interest in performance for the past 13 years. Over the years, he has helped a wide range of clients in areas such as front-end performance, load balancing and scalability, and database optimization. Past open source projects include modules for Apache and OSCommerce, a cross-platform IRC client, and contributions to The Linux Documentation Project (TLDP).

Tags:

Comments

2 Responses to “4 JavaScript Minifiers Compared”

  1. Anonymous says:

    Microsoft "ajax" minifier executed out of the box with no twiking, has actually slightly better results than all the listed options, with the exception of course of Clouse advanced mode.

  2. Tom says:

    In real world production use, we rely on Microsoft Ajax Minifier. It’s almost always the fastest and best compressor. Closure advanced mode should not be used in a production environment, so shouldn’t even be in the comparison.

Leave a Reply

What is 13 + 4 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)