Maximizing Your Local Variables and Functions

Modified on 2009/12/14 23:41 by Stephen Walther — Categorized as: Uncategorized

It’s not enough to take any JS file and run Microsoft Ajax Minifier on it. Yes, it should make your downloaded JavaScript file smaller, but there are way of writing your code that will maximize the benefits of minification.

For starters, the developer must recognize that global function and variable names are never renamed. If you code is written in the classic method of defining a series of global functions that call each other and act on global variables, only the local variables within each function will be renamed. A gain will be made, but not as much as could be. The proper coding style is to leave those functions as global those that are truly global, and to next all others as local. This not only will help with your code minification, it will also minimize the possibility of naming collisions between your code and any other JavaScript code that might be added to the web page.

The simplest way of doing this is to define a single global object in your script file as a namespace scope. Within that object, define “global” functions as methods on that object. Any other functions defined will be hidden from other modules as local functions, and therefore avoid naming collisions and be renamed for space gains.

Let’s see an example. Let’s say we are writing a JavaScript file that will provide two methods (“Start” and “Stop”) and a variable, “Status” for the markup or other code to be able to call or reference. There may be any number of other helper functions or variables, but none of them need to be exposed to any other modules:

var MyScope = new function()
    var my = this;
    var requestObject = null;

my.Status = 0;

my.Start = function( url ) { if ( my.Status == 0 ) { startRequest( url ); } else { alert("request processing"); } } my.Stop = function() { if ( my.Status != 0 ) { cancelRequest(); } } function startRequest( url ) { // kick off the request my.Status = 1; } function cancelRequest() { // cancel a pending request my.Status = 0; } };

In this code, MyScope is a global field and will therefore not be renamed, but every other variable and function will be renamed since they are all local to the MyScope object. Properties are not local fields and are therefore also not renamed; so the MyScope object will continue have the property “Status” and the methods “Start” and “Stop.” The variables “my” and “requestObject,” the argument “url,” and the helper functions “startRequest” and “cancelRequest” are all local and will be renamed. They can be as long and semantic as readability requires without having any impact on the client download performance. JavaScript code outside this module (or even within) can call the public methods and properties as MyScope.Status, MyScope.Start or MyScope.Stop. The internal local variables and functions are essentially private and not exposed to any other code outside the MyScope object.

The “my” local field is not necessary, but it’s a very valuable shortcut for these types of namespace objects. The “my” variable will be renamed, most-likely to a single character. If the developer instead used the this literal everywhere, it would not get renamed and would remain at four characters everywhere it is used. AjaxMin will take create a local variable, set it to the this pointer, and use that variable wherever the this pointer is used. It will only do this, however, within the scope directly applicable to the this pointer; it won’t propagate the generated local variable into child scopes because the context of the this pointer in child functions may not be the same.

Namespaces can be nested. At the Msn Portals team, we typically define our namespaces within a first-tier namespace of “Msn.” Our code files typically start off like this:

if (!window.Msn){Msn={};}
Msn.MyNamespace = new function() 
    // namespace object goes here

If your code is using the Atlas framework, there is even a helper function for defining the base namespaces: registerNamespace. It will determine if any of the namespace objects passed in to it need to be created or not, and if so, create them as empty objects. If you’re not using Atlas but plan on using multiple levels of namespaces, writing such a global helper function might be a good idea; it greatly simplifies namespace definitions:

Msn.Portals.MyScope = new function()
    // namespace object goes here

The general coding rule of thumb for maximum minification is: always wrap your code within a namespace, exposing only those functions and variables that truly are global (used by other modules or namespaces). Everything else – all your helper functions and state variables – should be declared as local to your namespace object.