Javascript Method Chaining, aka Fluent Interface

There are several ways to implement the Fluent Interface in JS, and this is one I was using in a project, and halfway forgot. (One of the problems with JS is that it’s got very little syntactic sugar, so you need to use conventions and notes to remember what you intended.)

This is strictly demo code, and useless.

RB = {}
RB.demo = {};
RB.demo.chainable = function chainable() {
    var internalVar;
    var externalVar;

    var thingChain = function initializer(arg1, arg2) {
        internalVar = arg1;
        externalVar = arg2;
        return thingChain;
    }

    thingChain.getArg1 = getInternalVar;
    function getInternalVar() {
        return internalVar;
    }

    thingChain.getArg2 = getExternalVar;
    function getExternalVar() {
        return externalVar;
    }

    thingChain.setInternalVar = setInternalVar;
    function setInternalVar(x) {
        internalVar = x;
        return thingChain;
    }

    thingChain.increment = increment;
    function increment() {
        internalVar++;
        externalVar++;
        return thingChain;
    }

    return thingChain;
};

var chain = RB.demo.chainable();
chain(100, 200);
console.log(chain.getArg1()); // 100
console.log(chain.getArg2()); // 200
console.log(chain.setInternalVar(50).getArg1()); //50
console.log(chain.setInternalVar(50).getArg2()); //200
console.log(chain.increment().getArg1()); //51
console.log(chain.increment().getArg2()); //202

var chain2 = RB.demo.chainable();
chain2(1,2);
console.log(chain2.getArg1()) // 1
console.log(chain2.getArg2()) // 2

(There is no difference between internalvar and externalvar. I thought I’d do something clever, but didn’t.)

This is similar to the pattern in this article at schier.co, except I don’t use the “new” keyword to create the instance. The chainable() function is a constructor, and the constructor returns an initalizer function.

The main advantage of this pattern is speed – the lookups for each method are restricted to the constructor function’s scope (since we should not use globals). The main disadvantage, as always, is increased memory usage.

How it works.

The main thing to notice is that the constructor creates a scope for the local, private variables. Then, the variable thingChain is set to the initializer function, which is also an object.

Onto the thingChain object, we set several properties, which are all functions. Some of them return the thingChain object, and that allows chaining. Others (the getters) return a value, and cannot have functions following them.

The fact that the chain acts like an object seems a little odd, because there’s no object created via “new” or constructed and returned like “return {foo:bar};”. That’s because the object is the scope that’s created when RB.demo.chainable() is invoked. That function scope is the “object”. A new function scope is create with every invocation.

The twist is that we don’t return the “object”, (mainly because we can’t get a reference to the function scope) but return the initializer() function instead.

Coding Style Comparisons

The above code typically results in the two-line pattern of:

var myobject = RB.library.path.to.theseobjects();
myobject( foo, bar, baz);

Myobject is now configured and ready to use. This is different from, but reminiscent of the more traditional:

var myobject = new TheseObjects(foo, bar, baz);

The other thing novices will find odd is:

thingChain.funcName = funcName;
function funcName(x) { .... }

We’re using funcName before it’s defined… or appears to be defined. Javascript has a feature called “hoisting” where uses of “function” are executed as if they’re up at the top of the scope.

I hated this for years, and wrote code like this:

thingChain.funcName = function() { ... };

In this code, the function definition happens where it exists in the code. It feels more sensible. The problem is, when you want to invoke funcName in another function:

thingChain.anotherFunc = function() { 
    thingChain.funcName();
};

At this point, you must be sure that anotherFunc is executing after thingChain.funcName is defined. I just instinctively put the called functions before they were used. That works most of the time, but sometimes, you need to make forward references to function names. While this generally isn’t a problem, it can become one if you intersperse function calls with function definitions. You shouldn’t do that, but it might happen.

Using the regular form of function to define functions has the advantages of allowing forward references, and also allowing for shorter function names:

function anotherFunc() { 
    funcName();
}

Now it looks more like C, a nice old language, rather than a pseudo-Java.
Forward references not a problem — you can arrange your code so it’s easier to read.