Chaining - Fluent Interfaces In JavaScript
Written by Ian Elliot   
Monday, 20 August 2012
Article Index
Chaining - Fluent Interfaces In JavaScript
Getting Complicated

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.

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 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 to much about why you would want to do things. Concentrate instead on how they work. The reasons and 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 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 function1, function2 and function3 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. 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.

Also notice that if you want to 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.

Singleton Chaining

Before we get on to more complicated examples, let's examine another simple case - singleton chaining. A singleton is a JavaScript object that isn't created by a constructor - it is an object literal.

In this case what we want to do is allow each function to be defined as a method of the same object - obj say. To allow chaining we have to arrange for each method to return an object that each subsequent function is defined on and in this case it is just obj. It really is this simple.

So let's define our object with three methods:

var obj = {
            function1: function () {
                alert("function1");
                return obj;
            },
            function2: function () {
                alert("function2");
                return obj;
            },
            function3: function () {
                alert("function3");
                return obj;
            }
        }

Notice that each function returns a reference to obj and this is what allows the next function call in the chain to work. With this definition you can write:

        obj.function1().function2().function3();

and you will see the three alert boxes indicating that each function is called in turn.

Instance Chaining

Singletons can be very useful, but often we want to be able to create as many instances of an object as desired and in this case we have to move on to look at instance chaining.

This is very similar to the singleton case, but now we have to create a constructor that will produce an instance of the object.

var objConstruct = function(){
            this.function1= function () {
                alert("function1");
                return this;
            };
            this.function2=function () {
                alert("function2");
                return this;
            };
            this.function3= function () {
                alert("function3");
                return this;
            }
        };

Notice that we have defined three methods for the object we are constructing, but also notice the use of "this" to reference the object being constructed.

What should each of the functions return to ensure that the next function can be called?

In this case each function has to return "this" because when the function is called "this" references the instance that the function is a method of.

Now we can write:

var obj = new objConstruct();
obj.function1().function2().function3();

and again we see the alert boxes announce that each function is called in turn.

If you can't see the difference between this and the singleton example, remember that we can now create as many instances of the object as we like. Also notice that because each function returns "this" as the object for the next function all of the function in the chain are called in the context of the same instance of the object.

Banner



Last Updated ( Monday, 20 August 2012 )
 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.