Understanding the concepts of JavaScript miniaturization

instacode

There is a lot of compression, miniaturization and optimization tools for JavaScript code. Reflecting GCC (cf. Will It Optimize? by Ridiculous Fish). But this tools do no magic so to make them more efficient, we must understand how those tools work to allow them to help us better in our quest for performance.


I will use the excellent UglifyJS to present some points thereafter.

Note: You will probably see that some numbers I will give will not be consistent with what you can see on your side. Those are numbers corresponding to a compression through Uglify while showed results are return provided by the command uglify -b. This is for the sake of this article readibility.

Readibility

In the following example, the code represents a point you can move upwards or downwards. It possesses a generalist method move which allow to move it to a number of pixels.

// Original version
function point() {
    var p = {};
    p.position = 0;
    p.move = function (pixels) {
        p.position += pixels;
    };

    p.up = function () { p.move(10); };
    p.down = function () { p.move(-10); };

    return p;
}

// uglify -b
function point() {
    var a = {};
    return a.position = 0, a.move = function(b) {
        a.position += b;
    }, a.up = function() {
        a.move(10);
    }, a.down = function() {
        a.move(-10);
    }, a;
};

Uglify : 142; Original : 233;

You can see the internal call to a.move? Often, when you will see unmodified names, it is that maybe an improvement can be done. By example, if we remove the public access to p.move by deleting its assignation to p:

// Original version
function point() {
    var p = {};
    p.position = 0;
    function move (pixels) {
        p.position += pixels;
    };

    p.up = function () { p.move(10); };
    p.down = function () { p.move(-10); };

    return p;
};

// uglify -b
function point() {
    function b(b) {
        a.position += b;
    }
    var a = {};
    return a.position = 0, a.up = function() {
        b(10);
    }, a.down = function() {
        b(-10);
    }, a;
};

Uglify : 136; Original : 230;

p.move(pixels) can now be modified in b, which gives us a more important compression. I admit it, for this example, the gain is minor but I wish you represent it to yourself in the case of a way more rich application, with a lot more of code and so a lot more of recurrence.

Alias

I have often been puzzled by this habits some developers have, as those from underscore.js and reqwest, to shorten variable access. Afterall, isn'it just a preservation from a few bytes to refer to doc_body instead of document.body?

Finally, it appears to me that compressors are not always able to alias object properties. Ie:

function logStyles() {
  var doc_style = document.body.style;
  console.log(doc_style.border,
    doc_style.margin,
    doc_style.padding);
}

… will return:

function logStyles() {
    var a = document.body.style;
    console.log(a.border, a.margin, a.padding);
};

However, if Uglify adopted the politic to alias every accessible properties of JavaScript objects, it would fastly fall in traps like:

// Original version
var mine = document.getElementById('mine');
var his = document.getElementById('his');
var hers = document.getElementById('hers');

// Naive and theoretical compression
var a = document.getElementById;
var mine = a('mine'),
    his = a('his'),
    hers = a('hers');

This stuff would give you a big fat Illegal Invocation since this is not document when a is executed.

Now, to be transparent to you, you could do this optimization in this way:

var a = function() {
    return document.getElementById.apply(document, arguments);
};

But I can't see the fun in this. Especially as to make getElementById avalaible strictly by procuration would do that function has its own properties, which is totally possible with JavaScript, so this optimisation would destroy them. It is not sure, neither practical.

Moral of the story: The compressors do not optimize properties because it would be weird and complex for them to do it. When you see an object property often, do a variable for that by yourself and you will give an explicit name as aliasToDocumentProperty - thus you would have the readibility and a good compression ratio.

Some little things

Do not get too much headaches with the choice of the ternary expressions and the form if / else. The local variable are often reduce so do not use variable with a single character name unless you really have a good reason or it's about x, y or i-ish stuff.

The old ghost about always use the keyword var for local variables is still present too: if you forget, your variable will be part of the global scope, so:

// Original
function forgetVar() {
    myLongVariableName = 2;
}

function dontForgetVar() {
    var myVeryVeryLongVariableName = 2;
}


// uglify -b
function forgetVar() {
    myLongVariableName = 2;
}

function dontForgetVar() {
    var a = 2;
}

And so on

Do not go hang to the idea that miniaturization is the golden ticket. It's fun, very useful but ideally your code is cached (sometimes in compiled form) the first time it's loaded.

Now, this miniaturization detail will really interest you if you are developing some applications targeting small bandwith devices (low bandwith wifi, mobile, …).

Feel free to comment here.