How to chain functions without using prototype?

2019-01-17 16:48发布

问题:

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

回答1:

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()...



回答2:

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


回答3:

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.



回答4:

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.