I have a bunch of useful functions that I have collected during my whole life.
function one(num){
return num+1;
}
function two(num){
return num+2;
}
I can call them with two(two(one(5)))
But I would prefer to use (5).one().two().two()
How can I achieve this without using prototype?
I tried to see how underscore chain works, but their code is too intense to understand it
The dot syntax is reserved for objects. So you can do something like
function MyNumber(n) {
var internal = Number(n);
this.one = function() {
internal += 1;
// here comes the magic that allows chaining:
return this;
}
// this.two analogous
this.valueOf = function() {
return internal;
}
}
new MyNumber(5).one().two().two().valueOf(); // 10
Or you're going to implement these methods on the prototype of the native Number object/function. That would allow (5).one()...
In order to avoid having to call toValue
at the end of the chain as in @Bergi's solution, you can use a function with attached methods. JS will call toValue
automatically when trying to convert to it a primitive type.
function MyNumber(n) {
function x () { }
x.one = function() { n++; return this; };
x.valueOf = function() { return n; };
return x;
}
Then,
MyNumber(5).one().one()
> 7
A nice and general alternative is creating a custom function composition function
var go = function(x, fs){
for(var i=0; i < fs.length; i++){
x = fs[i](x);
}
return x;
}
You can call it like this:
go(5, [one, two, two])
I am personaly not a big fan of method chaining since it restricts you to a predefined set of functions and there is kind of an impedance mismatch between values inside the "chaining object" and free values outside.
Another alternative is to use lodash flow function. For example:
var five = _.flow(one, two, two)
five(5)
I prefer assigning a new chain to a variable. It gives it a clear name and encourages re-use.
Btw, lodash also helps in passing additional arguments to the functions of the chain. For example:
var addFive = _.flow(
_.partialRight(_.add, 1),
_.partialRight(_.add, 2),
_.partialRight(_.add, 2)
)
There are many other useful functions to help in functional chaining, e.g., partial, spread, flip, negate, etc.