Search code examples
javascriptprototype

How to bind parameter, assigning function to class prototype


Let's say i have a function

function getSum (firstValue) { return firstValue + this.secondValue }

and some class

class TestClass {}

how can i dynamically assign function getSum to class prototype with binding firstValue as 1, so after

// some code like TestClass.prototype.getSum = getSum.bind(null, 1)
const obj = new TestClass()
obj.secondValue = 2
console.log(obj.getSum()) // 3

i could get 3

For object i could do it like this

obj.getSum = getSum.bind(obj, 1)

But for TestClass.prototype i can not set first parameter of bind because context is not existing yet Can this puzzle be solved directly?

Indirectly i can do something like this

const firstValue = 1
TestClass.getSum = function () {
  return getSum.bind(this, firstValue)()
}

or like this

TestClass.firstValue = 1
TestClass.getSum = function () {
  return getSum.bind(this)(TestClass.firstValue)
}

but may be it can be done more directly

Thanks in advance


Solution

  • You can create a function that takes one parameter will call getSum but supplies the first parameter itself.

    TestClass.prototype.getSum = function() { //<–– normal function
      return getSum.call( this,   1 );
    //              ^^^^  ^^^^    ^ 
    //               ||    ||     |
    // forward this –++––––++     +–––––– pass a value for the first parameter
    }
    

    This gets you the following

    function getSum (firstValue) { return firstValue + this.secondValue }
    
    class TestClass {}
    
    TestClass.prototype.getSum = function() {
      return getSum.call(this, 1);
    }
    
    const obj = new TestClass()
    
    obj.secondValue = 2
    console.log(obj.getSum()) // 3

    In general this the process where you supply bind a value to a parameter in the function is called partial application. If a function takes three parameters, for example, you can only set two at first and expect the final one at a later point. The whole process can be abstracted away by creating a function to handle this:

    function partiallyApply(fn, ...params) {
      return function(...moreParams) {
        return fn.call(this, ...params, ...moreParams);
      }
    }
    
    function takes4Parameters (a, b, c, d)  {
      return a + b + c + d;
    }
    
    const takes2Parameters = partiallyApply(takes4Parameters, 1, 2); // 1 + 2 + c + d
    
    console.log("1 + 2 + 11 + 12 =", takes2Parameters(11, 12));
    
    const takes1Parameter = partiallyApply(takes2Parameters, 3); // 1 + 2 + 3 + d
    
    console.log("1 + 2 + 3 + 5 =", takes1Parameter(5));
    
    const takesNoParameter = partiallyApply(takes1Parameter, 6); // 1 + 2 + 3 + 6
    
    console.log("1 + 2 + 3 + 6 =", takesNoParameter());

    Using that higher order function, we can more easily derive the getSum method for TestClass

    function getSum (firstValue) { return firstValue + this.secondValue }
    function partiallyApply(fn, ...params) {
      return function (...moreParams) {
        return fn.call(this, ...params, ...moreParams)
      }
    }
    
    class TestClass {}
    
    TestClass.prototype.getSum = partiallyApply(getSum, 1);
    
    //example of adding other partially applied methods:
    TestClass.prototype.getSum2 = partiallyApply(getSum, 2);
    TestClass.prototype.getSum3 = partiallyApply(getSum, 3);
    TestClass.prototype.getSum4 = partiallyApply(getSum, 4);
    
    const obj = new TestClass()
    
    obj.secondValue = 2
    console.log(obj.getSum());  // 3
    
    console.log(obj.getSum2()); // 4
    console.log(obj.getSum3()); // 5
    console.log(obj.getSum4()); // 6