Larry Steinle

August 18, 2013

JavaScript Chaining Made Fun!

Filed under: Web — Larry Steinle @ 6:59 pm
Tags: , , , ,

The chain pattern allows several actions to be executed within a single line of concurrent code. jQuery’s use of chaining has popularized the concept so that the masses now prefer chaining their method calls. Creating an application that supports chaining can be cumbersome. This article will demonstrate how to add chaining functionality to any JavaScript class without modifying any code in the class you want to upgrade to support the chaining pattern.

Standard Calculator Class

We will begin with a simple calculator class. Our calculator class will support addition, subtraction, division and multiplication.

var myCalc = function (v) {
  var _s = (!v) ? 0 : v;
  return {
    Clear: function () { _s = 0; },
    Plus: function (v) { _s = _s + v; },
    Minus: function (v) { _s = _s - v; },
    Times: function (v) { _s = _s * v; },
    DividedBy: function (v) { _s = _s / v; },
    toString: function () { return _s; }
  };
};

There is nothing special about this class at this time. Note only that we store the current value in a private variable which is accessible only when calling the toString method. We have taken absolutely no effort to make this class work for chaining and indeed at this time it can’t be chained!

Now we can test our class to see how the method calls will work and that the result returns as expected.

var calc = new myCalc(1); //Initialize the calculator value to 1
calc.Plus(15);
calc.Minus(4);
alert(calc.toString());   //Displays a value of 12

Make any Class Chainable

In order for a class to be chainable the following requirements must be met:

  • Each function must return a reference to the class.
  • The class must have access to the last available value(s) from the last function call.

In nearly all chainable examples I have seen the result is returned as an array. In our example I leave it up to you to decide if you want to track all results in an array or not. But to keep things easier we won’t worry about array return values from our chainable classes.

The following function converts any class into a chainable object. Simply pass the class that you want to make chainable into the _MakeChainable function and the returned object is a chainable shallow copy of the original object.  A shallow copy means that the properties and functions at the root level of the object are copied. A deep copy implies that any child classes and their properties and functions are copied as well. Again, for the sake of our sample we won’t worry about deep copies. However, if you require a deep copy a simple recursive routine can be used to attain the deep copy.

var _MakeChainable = function (c) {
  var _v, _o = { _get: function () { return _v; }, _set: function (v) { _v = v } };

  for (var i in c) //Iterate thru each function in the class
    _o[i] = (function (fn) {
      var _fn = fn;  //Retain a reference to the function to chain
      return function () {
        _o._set(_fn.apply(this, arguments)); //Save any returned value into our private value argument
        return _o;                          //Return a reference to our own chainable copy of the class
      };
  })(c[i]);

  return _o;
};

A class is made chainable by creating a “wrapper” function around each function in the class. The “wrapper” function will be responsible for tracking the return value from the function (which is stored in the _v variable) and returning a reference to the local copy of the chainable class (which is stored in the _o variable).

The only limitation to the _MakeChainable function is that the class passed in can’t have a _get or _set public method. These two methods are reserved by _MakeChainable.

That’s all it takes to make any class chainable! But does it work?

It’s Really That Simple!

Now to make our calculator chainable! Simply pass the standard calculator class into our method and use the returned chainable class to perform the calculations!

var calc = _MakeChainable(new myCalc(1)); //Initialize the calculator value to 1 and make it chainable
alert(calc.Plus(15).Minus(4).toString()); //Shows undefined!

When the alert is called there will be no error however an undefined value will be displayed. That’s because the functions don’t return any values any more. In order to access the values you must call the _get method that was added by our _MakeChainable function.

If the returned value needs to be passed on to the next function we must call the _get method as an argument to the next call. This seemingly apparent limitation serves a very important purpose. Since the class we want to chain wasn’t coded to support chaining it is probable that the value from the function isn’t supposed to be passed to the first argument in the next function. So the developer remains in control of which argument in the function to pass the previous functions return value.

We still need to call the toString method from the calculator class because none of the classes returned the value. But to access the returned value we need to simply add one more chainable method call to the reserved _get function and voila’, the chainable class returns the correct value!

var calc = _MakeChainable(new myCalc(1)); //Initialize the calculator value to 1 and make it chainable
alert(calc.Plus(15).Minus(4).toString()._get()); //Correctly shows 12!

Summary

Today’s article demonstrated a little bit of crafty magic to make any JavaScript class chainable. As we saw implementing the chainable pattern doesn’t have to be time consuming or difficult. We can develop our javascript classes without even thinking of chaining and use chaining when it is helpful. IMHO we have demonstrated the easiest and best way to implement the chainable pattern as it is in full compliance with the Seperation of Concerns (SoC) principle and the Single Responsibility principle.

Happy coding!

Advertisement

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: