More Thoughts on Compression

Last weeks article, Development - Optimizing JavaScript Compression and the user comments, plus Nick Zakas document on how to improve YUI compression performance, Helping The YUI Compressor, left me thinking about other ways to improve the compression of YUI compressor. We learned last week that the compressor will obfuscate local functions, defined as variables, into variables with names that are only single characters. Extending that idea further I realized that if an object member function is repeatedly used in a file, then it too can be reduced. This thought led to a simple equation that determines when it is more efficient (compression-wise) to replace the use of a member function with a local variables.

Example 1: Compression Optimization Equation

L + C + (K * N) = L * N, where L > K and
L = the length of the function name + 1
C = the length of code to create a shortcut variable
K = the length of the replacement code, per instance of the function name
N = the number of times the function is used before this technique improves compression
N = (L + C) / (L - K)

Lets walk through an example, in case that is hard to follow. A frequently used function is the native DOM function getElementsByTagName. This method is 20 characters long, and since it is always proceeded with a . when used, add 1 (ex. "_domNode.getElementsByTagName"), so L equals 21 (eg. each time getElementsByTagName is normally used, 21 characters are required, even after compression). Now this can be replaced with a reusable variable, using brackets and a variable storing the string name of the function.

Example 2: Optimization Technique

var GEBTN = getElementsByTagName;
_domNode[GEBTN](li);
_domNode[GEBTN](span);
_domNode[GEBTN](small);

Compression will reduce the GEBTN variable name to a single character and remove whitespaces, so the cost of initializing GEBTN will be C = 29. If you are using the "Use fewer var" technique from Example 1 in last weeks article and can instantiate the GEBTN by piggybacking an already existing var, then the cost of initialization drops by 3 ("var " is replaced with ","), C = 26. Then each usage of the compressable technique to replace getElementsByTagName will require 3 characters, K = 3 ("[GEBTN]" when compressed will become something like "[A]"). Putting this into the equation and solving for N, we find that for getElementsByTagNameN is equal to 2.6 = 47 / 18 = (21 + 26) / (21 - 3). Therefore, if you use getElementsByTagName 3 or more times in a file, this compression technique will allow YUI compressor to reduce the file size even more.

The equation in Example 1 only holds true for functions with name that are larger than 3 characters, otherwise this technique will not help. You might wonder why "_domNode.getElementsByTagName" is not saved as a variable function, instead of just the name of the member function. The reason is because when the variable function executes, the execution context (this) will be window instead of the desired context (in this case the desired execution context _domNode). If you are trying to eek every byte of compression out of your JavaScript, then this equation will be a handy and easy tool to evaluate which member function replacements make sense and which do not.

Finally, the response to last weeks article was great. I would appreciate hearing about any other good techniques to help compression. Please leave a comment if you know of one.