Search code examples
javascriptchaining

Best way to implement Javascript chaining in a library


I'm creating a JavaScript library. I've been trying to implement chaining.

0: What I first came up with:

function V(p) {
  return {
    add : function(addend) { return V(p + addend); },
    sub : function(subtra) { return V(p - subtra); },
  };
}

Using this method I can chain easily:

V(3).add(7).sub(5) // V(5)

Unfortunately the result is always a wrapped V() function, I am unable to extract the resulting value this way. So I thought about this problem a bit and came up with two semi-solutions.

1: Passing flag to last method

function V(p, flag) {
  if(flag)
    return p;
  else
    return {
      add : function(addend, flag) { return V(p + addend, flag); },
      sub : function(subtra, flag) { return V(p - subtra, flag); }
    };
}

Using this method I can end the chain by passing a flag to the last method I use:

V(3).add(7).sub(5, true) // 5

While this works just fine, it requires some code repetition and makes chaining less readable and my code less elegant.

2: Using start() and end() methods

_chain = false;
function V(p) {
  function Wrap(w) {
    return (_chain) ? V(w) : w;
  }
  return {
    add : function(addend) { return Wrap(p + addend); },
    sub : function(subtra) { return Wrap(p - subtra); },
    start : function() { _chain = true; },
    end : function() { _chain = false; return p; }
  };
}

Using this method you can do single operations with no more code:

V(3).add(7) // 10

But chaining requires two more methods, making things a lot less readable:

V(3).start().add(7).sub(5).end() // 5

So basically I'm just searching for the best way to implement chaining into my library. Ideally I'm looking for something where I can use any number of methods and don't need to terminate the chain in inelegant ways.

V(3).add(7).sub(5) // 5, perfect chaining

Solution

  • Why not introducing a private variable and work on that? I guess that is even more convenient. Plus it's probably a good idea to have a pure "getter" that finally returns the computed value. This could look like this:

    function V(p) {
      var value = p;
    
      return {
        add: function(addend) {
          value += addend;
          return this;
        },
        sub: function(subtra) {
          value -= subtra;
          return this;
        },
        get: function() {
          return value;
        }
      };
    }
    
    console.log(V(3).add(7).sub(5).get()); // 5

    You cannot return the Object in a getter function obviously. So you need some method where the chaining ends and returns a value.