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.