JavaScript: Method Chaining and Function Composition

By Xah Lee. Date: . Last updated: .

In JavaScript, we can do method chaining. That is, the dotted syntax:

x.f().f2().f3()

This works because:

  1. the value of x is a object and has a method f.
  2. the value of x.f() is a object and has a method f2.
  3. the value of x.f().f2() is a object and has a method f3.

If you want to write a library that allow method chaining, just make sure your methods all return the same object X of your lib, and all the methods are properties of X.

Method chaining can be considered as a postfix notation. For example, similar to unix pipe x | f | f2 | f3.

The problem with method chaining is that it relies on the fact that the preceding sequence must return a object that has the next method as property.

You cannot chain functions A and B with the dot notation if A and B has no object/property relation.

However, you can write a function that does this.

Postfix Function Composition

Here's a way to chain and functions x f1 f2 f3 etc, without them being a property of the return object.

  1. postfixChain(x) ⇒ x
  2. postfixChain(x,f) ⇒ f(x)
  3. postfixChain(x,f,f2) ⇒ f2(f(x))
  4. postfixChain(x,f,f2,f3) ⇒ f3(f2(f(x)))

Here's the code, ES2015 version.

const postfixChain = (( ...rest) => {

/* (
   postfixChain(x) → x
   postfixChain(x,f) → f(x)
   postfixChain(x,f,f2) → f2(f(x))
   postfixChain(x,f,f2,f3) → f3(f2(f(x)))
   etc

   http://xahlee.info/js/js_function_chaining.html
   version 2017-04-22
) */

    let ff = Array.prototype.shift.call ( rest);
    while ( rest.length > 0) { ff = (Array.prototype.shift.call( rest))(ff); }
    return ff;

});

// -------------------------------
// test

const fa = (x => (x + "a"));
const fb = (x => (x + "b"));
const fc = (x => (x + "c"));

console.log( postfixChain("0") ); // prints "0"
console.log( postfixChain("0", fa) ); // prints "0a"
console.log( postfixChain("0", fa, fb) ); // prints "0ab"
console.log( postfixChain("0", fa, fb, fc) ); // prints "0abc"

Here's the ES5 version.

var postfixChain = function () {
/* (
   postfixChain(x) → x
   postfixChain(x,f) → f(x)
   postfixChain(x,f,f2) → f2(f(x))
   postfixChain(x,f,f2,f3) → f3(f2(f(x)))
   etc

   http://xahlee.info/js/js_function_chaining.html
   version 2017-04-22
) */
    var ff = Array.prototype.shift.call(arguments);
    while (arguments.length > 0) {
        ff = (Array.prototype.shift.call(arguments))(ff);
    }
    return ff;
};

// -------------------------------
// test

var fa = function (x) { return x + "a"; };
var fb = function (x) { return x + "b"; };
var fc = function (x) { return x + "c"; };

console.log( postfixChain("0") ); // prints "0"
console.log( postfixChain("0", fa) ); // prints "0a"
console.log( postfixChain("0", fa, fb) ); // prints "0ab"
console.log( postfixChain("0", fa, fb, fc) ); // prints "0abc"

see also JavaScript: Functional Programing

Like what you read? Buy JavaScript in Depth
or, buy a new keyboard, see Keyboard Reviews.