Non-programmer readers, might want to skip this one, it’s pretty technical and relatively obscure.

my dinosaur, back when I had my Yashica

Anyway, back to the thing at hand. A while ago I read Will it Optimize? by the excellent ridiculous fish. It’s a fun adventure into what code GCC recognizes and optimizes, and what it doesn’t, or can’t.

This is a much simpler version of that, for Javascript, for size. I’ve been maintaining Wax, a mapping library that’s meant to be tiny, and slowly learning the tricks of the trade for writing tiny Javascript - not 140bytes-type evil tricks, but practical optimizations. Like how you get a library down to tiny sizes without making it a headache to maintain or making errors in the non-minified source untraceable.

Most of this comes down to understanding the magic of uglifyjs, the most interesting Javascript project of recent times.

I’ll cut to the chase, using uglifyjs -b for example minification.

Visibility

This is a car that can turn left or right, and has a more general function turn , that can turn any number of degrees.

function driver () { var d = {}; d . direction = 0 ; d . turn = function ( degrees ) { d . direction += degrees ; }; d . right = function () { d . turn ( 90 ); }; d . left = function () { d . turn ( - 90 ); }; return d ; }

uglifyjs does its best: 147 chars, from 228: 64%

function driver () { var a = {}; return a . direction = 0 , a . turn = function ( b ) { a . direction += b ; }, a . right = function () { a . turn ( 90 ); }, a . left = function () { a . turn ( - 90 ); }, a ; }

But see how this is calling a.turn() internally? Often seeing non-mangled names means that there’s a tweak you can make. For instance, if you eliminate public access to d.turn , by removing its assignment to d :

function driver () { var d = {}; d . direction = 0 ; function turn ( degrees ) { d . direction += degrees ; } d . right = function () { turn ( 90 ); }; d . left = function () { turn ( - 90 ); }; return d ; }

Becomes

function driver () { function b ( b ) { a . direction += b ; } var a = {}; return a . direction = 0 , a . right = function () { b ( 90 ); }, a . left = function () { b ( - 90 ); }, a ; }

d.turn(degrees) can now be mangled into just b , so the compression becomes 131 chars from 220: 59% of its original size, by optimizing internal calls. It’s minor, in this instance, but add a lot of repetitive code and this difference can add up.

Aliasing

I was always mystified by how impressive libraries like underscore.js and reqwest would shortcut access to variables. After all, isn’t it just saving minor bytes to refer to doc_body instead of document.body ?

It eventually dawned on me: minifiers aren’t always able to alias object properties. For instance,

function logStyles () { var doc_style = document . body . style ; console . log ( doc_style . border , doc_style . margin , doc_style . padding ); } // after uglify -b, line-broken function logStyles () { var a = document . body . style ; console . log ( a . border , a . margin , a . padding ); }

However, if uglifyjs were to make the policy that it should alias all property-accessed objects in Javascript, then it would immediately fall into traps like:

// unminified var mine = document . getElementById ( ' mine ' ); var his = document . getElementById ( ' his ' ); var hers = document . getElementById ( ' hers ' ); // theoretical naïve minification var a = document . getElementById ; var mine = a ( ' mine ' ), his = a ( ' mine ' ), hers = a ( ' mine ' );

Which gives you a big, fat ‘Illegal invocation’, since this isn’t the document when a is run.

Now, to be clear, you could optimize to

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

But that isn’t fun at all, and strictly proxies getElementById - if the function has properties of its own, as they are plenty allowed to do in Javascript - then this optimization will kill them. So, it’s unsafe and big.

Moral of the story: minifiers don’t optimize object properties because it would be weird and hard for them to do it. When you use an object property a lot, make a variable for it yourself, and you can give that variable a descriptive name, like aliasToDocumentPropertyX - so you can have both readability and good minification ratios.

The Little Things

Don’t care that much about ternary form versus if / else . Local variables are reliably shortened, so don’t use single-char names unless you have very good reason, or they’re i , x or y .

The old wisdom about always using var for local variables is here too: if you forget, your variable name is in the global scope, so

// unminified function forgetVar () { myLongVariableName = 2 ; } function dontForgetVar () { var myVeryVeryLongVariableName = 2 ; } // after uglify -b function forgetVar () { myLongVariableName = 2 ; } function dontForgetVar () { var a = 2 ; }

And so on

Don’t get caught up in minimization thinking it’s the golden ticket: it’s fun and useful, but ideally your javascript is cached - sometimes in compiled form - the first time it’s loaded.

But, if you’re writing code that aims to be invisible and uber-light for bad connections or to be thrown along with every page, it’s useful to grow an understanding of what your minifier can and can’t do.

I’m no minifier expert: if you’ve got any knowledge to share, please add it to comments or link it up. Have fun!