JavaScript is an amazing language

2011-02-10

When I changed jobs recently I bet on working as a JavaScript developer.

Part of my reasoning was that JavaScript is such a dynamic language that no matter what enterpricey or badly designed systems I will encounter, I will be able to program my way out of the mess and restore my sanity :-)

Today it seems to me that I must have been right.

On my new project I have to use a mix of Dojo, ExtJS and ArcGIS libraries.

I am not very impressed by ExtJS. It is too bloated for my tastes. I also think that Dojo and ExtJS documentation are partially to blame for the fact that I found my colleagues' code littered with object literals nesting 16+ levels deep. Because that's what you get if you start from code examples in documentation (e.g. dojo.declare, Ext.Container).

But with JavaScript it is fixable. That's what I did to refactor the code:

Fix #1: Self-executing anonymous functions instead of object literals

Lets say we have this module definition in code:

dojo.declare("MyModule", [], {
    method1: function(params) {
        // some code
    },
    
    method2: function(params) {
        // some code
    }
});

Looks nice until the methods grow in depth with UI element definitions, event handlers, etc.. Then you have a mess.

So I changed it to this:

dojo.declare("MyModule", [], (function() {
    
// I chose "exports" for similarity to CommonJS:
var exports = {};
    
exports.method1 = function(params) {
    // some code
};
    
exports.method2 = function(params) {
    // some code
};
    
// Bonus: private static vars.
var internalVar;
    
// Bonus: private static functions.
function internalFunction() {
    // some code
}
    
return exports;
    
})() // This calls the function we just created
     // and returns its result (exports object).
); // closing dojo.declare()

Seems longer but it is better structured, reduces deep nesting and lets you hide some variables and functions inside the module.

Fix #2: that = this and closures

Dojo includes a nice mechanism for injecting scope into functions: dojo.hitch().

However I found this abused in our code. So I remembered the good old trick and used it instead:

exports.someMethod = function() {
    
    var that = this;
    ...
    
    var someEventHandler = function() {
        
        // We need to go deeper:
        var anotherEventHandler = function() {
            
            that.callSomeOtherMethod();
            // At this level it saved me
            //  2 calls to dojo.hitch().
        }
    }
    ...
    
}

Fix #3: you can use functions before you define them (duh)

I needed to write code for a widget. The code involved Model definition (Ext.data.JsonStore), View definition (Ext.XTemplate) and widget definition:

exports.createMyWidget = function() {
    
    // Model (not shown)
    // View (not shown)
    
    // Widget mixed with event handlers:
    return Ext.Container({
        ...
        listeners: {
            click: function() {
                // lots of event handling here
            }
        }
    });
}

The "click" and other event handlers looked too heavy compared to other code, so I decided to move them into separate functions. And I did. To preserve code reading flow I moved them below the return statement:

exports.createMyWidget = function() {
        
    // Model (not shown)
    // View (not shown)
    
    // Widget:
    return Ext.Container({
        ...
        listeners: {
            click: onClick // defined below
        }
    });
    
    // Event handlers:
    function onClick() {
        // lots of event handling here
    }
}

Seems like a trivial thing, but very nice. Now I feel more confident about managing complexity in my work.