Page 1 of 3 One of jQuery's attractive features is the way that you can build up chains of commands that look almost like the way you would use a language. This is more generally called a "Fluent Interface" and it isn't difficult to implement in JavaScript.
Now available as a book from your local Amazon.
JavaScript Jems: The Amazing Parts
Contents
<ASIN:1871962579>
<ASIN:1871962560>
<ASIN:1871962501>
<ASIN:1871962528>
Jem 12
Fluent Interfaces
“Our business in living is to become fluent with the life we are living, and art can help this.”
John Cage
One of JavaScript’s attractive features is the way that you can build up chains of commands that look almost like the way you would use a language. This is more generally called a "Fluent Interface" and it isn't difficult to implement.
The idea of a fluent interface was first coined by Eric Evans and Martin Fowler to express the idea of an object-oriented API. The technique uses method chaining, but if you want to call it a fluent interface it also has to have the characteristics of a DSL, Domain Specific Language. Put simply, the way that the methods chain together has to express natural sets of operations. Ideally the chains of methods should read as if you were writing a tiny program with verbs and modifiers.
First let's take a look at the mechanism of chaining. In what follows don't worry too much about why you would want to do things. Concentrate instead on how they work. The advantages of chaining and fluent interfaces will become clear later.
General Chaining
Chaining is where you call one function after another. For example:
function1().function2().function3()
and so on. It is usual, but not necessary, for the first function to be a method belonging to a particular object:
myObject.function1().function2().function3()
and so on.
The object version of the chaining is the common idiom in other languages where functions aren't first class objects. In JavaScript, however, functions are first class objects, see Jem 3: Functions Are Objects, and this makes the first form of chaining perfectly reasonable.
When you first see something like this, you can't help but wonder why it all works. Indeed, it doesn't work unless you arrange for the functions to fit together.
The principle of chaining is that each function has to return an object, which supports the next function in the chain as a method.
This is all there is to function chaining. It’s simple, but it is easy to become confused in real cases. Take, for example, the chain:
function1().function2().function3()
For this to work function1 has to return an object which has function2 as a method. For simplicity, suppose obj2 has function2 as a method and obj3 has function3 as a method. First we need to define function1:
function1 = function(){
alert("function 1");
return obj2;
}
As promised, this returns obj2 which is defined as:
var obj2 = {
function2: function () {
alert("function 2");
return obj3;
}
};
which, again as promised, returns obj3 which is defined as:
var obj3 = {
function3: function () {
alert("function 3");
return obj3;
}
};
With these definitions you can now write:
function1().function2().function3();
and when you run the program you will see three alert boxes announcing three functions in turn. Of course, in practice the functions could have parameters and the objects could be generated by constructor functions requiring that we use new to create a new instance of each object to return.
Notice that it is the object returned by each function that gives the next function in the chain its context. That is, you can determine exactly what function is called by the object you return, so not only is this set to the object you have returned, but the function that is used depends on the object you return. For example, if the function was a search operation, then the display function called next could be automatically varied according to the type of object returned by the search. That is you call .display but the function you get depends on the object returned. This is a sort of natural polymorphism that all dynamically-typed languages have. Also notice that if you define function1 as a method of obj1 then this works, but now you have to start the chain with obj1:
obj1.function1().function2().function3();
This is all there is to function chaining and it all depends on each function returning an object which has the next function among its methods. However, this isn't the most common way that function chaining is used.
|