Composing functions allows us to build complex functions from many simple, generic functions. By treating functions as building blocks for other functions, we can build truly modular applications with excellent readability and maintainability.
Before we define the compose()
polyfill, you can see how it all works with these following examples:
var roundedSqrt = Math.round.compose(Math.sqrt) console.log( roundedSqrt(5) ); // Returns: 2 var squaredDate = roundedSqrt.compose(Date.parse) console.log( squaredDate("January 1, 2014") ); // Returns: 1178370
In math, the composition of the f
and g
variables is defined as f(g(x))
. In JavaScript, this can be written as:
var compose = function(f, g) { return function(x) { return f(g(x)); }; };
But if we left it at that, we would lose track of the this
keyword, among other problems. The solution is to use the apply()
and call()
utilities. Compared to curry, the compose()
polyfill is quite simple.
Function.prototype.compose = function(prevFunc) { var nextFunc = this; return function() { return nextFunc.call(this,prevFunc.apply(this,arguments)); } }
To show how it's used, let's build a completely contrived example, as follows:
function function1(a){return a + ' 1';} function function2(b){return b + ' 2';} function function3(c){return c + ' 3';} var composition = function3.compose(function2).compose(function1); console.log( composition('count') ); // returns 'count 1 2 3'
Did you notice that the function3
parameter was applied first? This is very important. Functions are applied from right to left.
Because many people like to read things from the left to the right, it might make sense to apply the functions in that order too. We'll call this a sequence instead of a composition.
To reverse the order, all we need to do is swap the nextFunc
and prevFunc
parameters.
Function.prototype.sequence = function(prevFunc) { var nextFunc = this; return function() { return prevFunc.call(this,nextFunc.apply(this,arguments)); } }
This allows us to now call the functions in a more natural order.
var sequences = function1.sequence(function2).sequence(function3); console.log( sequences('count') ); // returns 'count 1 2 3'