“add” function that works with different combinations of chaining/arguments

前端 未结 4 1839
忘掉有多难
忘掉有多难 2020-11-28 13:09

I\'m trying to write an add function that will work in many scenarios.

add(2,2,2) //6
add(2,2,2,2) //8
add(2)(2)(2) // 6
add(2)(2)(2,2).value() //8
add(2,2)(         


        
相关标签:
4条回答
  • 2020-11-28 13:14

    I have tried to improvise with the use of this. Works for all cases.

    function add(){
        var sum =  this instanceof Number?this: 0;
        for( var i in arguments ){
            sum += arguments[i];
        }
    
        var ret = add.bind(sum);
        ret.add = ret;
        ret.value = ret.valueOf = function() { return sum; };
    
        ret.toString = sum.toString.bind(sum);
    
        return ret;
    }
    

    JS-Fiddle

    0 讨论(0)
  • 2020-11-28 13:19

    Since you are returning the sum in the ret.add function thats why the error is coming try something like this, hope it will solve your problem

    function add(){
        var sum = 0;
        for( var i in arguments ){
            sum += arguments[i];
        }
    
        var ret = add.bind(null, sum);
    
        ret.value = function () {
          return sum;
        }
    
        ret.add = function () {
          for( var i in arguments ){
            sum += arguments[i];
          }
          return ret;
        }
    
        ret.valueOf = function(){ return sum; };
    
        return ret;
    }
    
    0 讨论(0)
  • 2020-11-28 13:23

    Looking at the way you're using arguments in similar ways in two different places, it's clear that you are duplicating functionality and that is why you are running into this problem with having to "infinitely nest" the .value() method.

    The key thing to recognize is that add() can return a function that references itself as its own add property. This will allow add(1,2)(3) to behave exactly the same as add(1,2).add(3). This can be done like so:

    function add() {
      var sum = Array.prototype.reduce.call(arguments, function(l, r) {
        return l + r;
      }, 0);
    
      var ret = add.bind(null, sum);
      ret.add = ret;
    
      ret.value = ret.valueOf = Number.prototype.valueOf.bind(sum);      
      ret.toString = Number.prototype.toString.bind(sum);
    
      return ret;
    }
    
    snippet.log(add(2,2,2));
    snippet.log(add(2,2,2,2));
    snippet.log(add(2)(2)(2));
    snippet.log(add(2)(2)(2,2).value());
    snippet.log(add(2,2)(2) + 2);
    snippet.log(add(2).add(2));
    snippet.log(add(2,2,2).add(2).value());
    snippet.log(add(2,2,2).add(2).add(2,2).value());
    
    snippet.log(add(1, 2, 3)(4, 5).add(6, 7)(8).add(9, 10));
    snippet.log(add(5,4)(3).add(2)(1) * 10);
    <!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
    <script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>


    There are still two potential issues with the above approach, one minor and one a little less minor:

    • There are property references and function definitions that are re-executed every time the add function is used (including during chaining)
    • If someone overwrites the add identifier, it would cause the whole implementation to break:

    function add() {
      var sum = Array.prototype.reduce.call(arguments, function(l, r) {
        return l + r;
      }, 0);
    
      var ret = add.bind(null, sum);
      ret.add = ret;
    
      ret.value = ret.valueOf = Number.prototype.valueOf.bind(sum);
      ret.toString = Number.prototype.toString.bind(sum);
    
      return ret;
    }
    
    var myAdd = add;
    add = "boom!";
    myAdd(1, 2, 3); // TypeError: add.bind is not a function

    Both of these can be remedied with an IIFE:

    var add = (function () {
        var reduce = Array.prototype.reduce,
            np = Number.prototype,
            valueOf = np.valueOf,
            toString = np.toString,
            plus = function (l, r) { return l + r; };
    
        return function add() {
            var sum = reduce.call(arguments, plus, 0);
    
            var ret = add.bind(null, sum);
            ret.add = ret;
    
            ret.value = ret.valueOf = valueOf.bind(sum);      
            ret.toString = toString.bind(sum);
    
            return ret;
        }
    })();
    
    var myAdd = add;
    add = "U Can't Touch This";   // hammertime
    
    snippet.log(myAdd(1, 2, 3)(4, 5).add(6, 7)(8).add(9, 10));
    snippet.log(myAdd(5,4)(3).add(2)(1) * 10);
    <!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
    <script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

    0 讨论(0)
  • 2020-11-28 13:29

    Also they need to always return ints (not strings), and it seems like sometimes they do and other times they don't?

    Yeah, this is definitely a conceptual problem. These two things you want aren't compatible. Is add(2,2,2) a number or something with an add method?

    add(2,2,2) //6
    add(2,2,2).add(2).value() //8
    

    Even if there is a fancy way to add methods to nubmers, I would highly recommend keeping things simple and always requiring a ".value()" call to end the chain. This way all calls to ".add" return an "adder object" and all calls to ".value" return a regular number.

    it seems like I would have to keep nesting the add functions if I wanted to chain more than two together and also add the value function to each of them, but obviously I'm missing something simple that will allow me to chain them as much as I like, and call value on any of them.

    The answer to this is to use recursive functions. Here is a function that creates the "adder" object I mentioned previously:

    function sumArray(arr){
        var s = 0;
        for(var i=0; i<arr.length; i++){
            s += arr[i];
        }
        return s;
    }
    
    function mkAdder(currSum){
        return {
            value: function(){
                return currSum;
            },
            valueOf: function(){
                return currSum;
            },
            add: function(/**/){
                return mkAdder(currSum + sumArray(arguments));
            }
        }
    }
    

    Then your initial add function would look like this:

    function add(/**/){
        return mkAdder(sumArray(arguments));
    }
    
    0 讨论(0)
提交回复
热议问题