Decoupling JS - using Lambdas, Closures and Scope Resolution
This post is the 2nd in a series of posts about decoupling JS code. You can find the table of contents here
In the previous post we disscussed how we can use Classes to better structure our code. In this post we will go into a much more fundumental part of JS's features.
JS's implementation of lambdas and scope resolution rocks in a very wide verity of ways, and so I will attempt to list some of them.
The basics
First – as I'm sure you've heard (and experienced), functions are first-class object. This means we can pass a function to another object, and it can use it as it will. This allows us to change the behavior of well structured code in a meaningful way, while keeping the anonymity of the passed function. This might sound complex, and I want to give an example that will make actual sense. For this example I'm going to use a snippet that uses a Mootools class, but the mechanism is pure JS so I hope you can bare with me
var List = new Class({
options : {
beforeOpenFunction : function(el){}
' openFunction : function(el){}
}
, initialize : function(el,opts){
this.setOptions(opts);
this.element = $(el);
}
, open : function(){
this.options.beforeOpenFunction(this.el);
this.element.setStyle('display','block');
this.options.openFunction(this.el);
}
});
var list = new List('someid',{
beforeOpenFunction : function(el){
el.setStyle('height',0);
}
, openFunction : function(el){
el.tween('height',0,100); //tween is an animating method. this one will animate the element's height between 0 and 100 pixels
}
});
In the above example, we create a mechanism with which a user can define a costume effect for the Class. The Class doesn't care what happens within the function (in fact, the default behavior does nothing), as long as it can call it.
Using scope resolutions
Next, let us talk about JS's scope resolution. Say we want to grant a certain object access to another object, but we don't want our objects to actually hold instances of one another. With JS, this is quite simple –
function Obj1(cb){
var result = this.performSpecializedAction();
cb(result);
}
function Obj2(){
this.state = '';
this.log = function(){
console.log(this.state);
}
}
var ob2 = new Obj2
, ob1 = new Obj1(function(res){
ob2.state = res;
});
Again – the key principle is that the Obj1 has no knowledge of what happens within the passed function, and doesn't care. Also, Obj1 can only interact with Obj2 in a way that was defined externally and implicitly. None of the two have any knowledge or control of the other.
The above tools are powerful on their own, especially when we built our object with designed hooks for such interaction. These hooks can be as simply as the above options argument or they can be supplied callback arguments, but they become truly powerful (in my opinion at least), when we start writing event-based code.