“添加”功能,适用于链接/参数的不同组合

时间:2015-03-08 14:24:16

标签: javascript

我正在尝试编写一个可在许多场景下工作的添加功能。

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)(2) + 2 //8
add(2).add(2) //4
add(2,2,2).add(2).add(2,2).value() //12
add(2,2,2).add(2).value() //8

这是我到目前为止所做的:

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 sum;
    }

    ret.valueOf = function(){ return sum; };
    
    return ret;
}

console.log(add(2,2,2));
console.log(add(2,2,2,2));
console.log(add(2)(2)(2));
console.log(add(2)(2)(2,2).value());
console.log(add(2,2)(2) + 2);
console.log(add(2).add(2));
console.log(add(2,2,2).add(2).value());
console.log(add(2,2,2).add(2).add(2,2).value());

我遇到过最后两种情况的问题:

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

似乎我必须继续嵌套添加函数,如果我想将两个以上链接在一起并且还为每个函数添加值函数,但显然我遗漏了一些简单的东西,这将允许我链接它们尽我所能,并且对其中任何一个都有价值。

此外,他们需要始终返回整数(而不是字符串),似乎有时他们会这样做,有时他们不这样做?

4 个答案:

答案 0 :(得分:16)

看看你在两个不同的地方以类似的方式使用arguments的方式,很明显你正在复制功能,这就是为什么你必须“无限地嵌套”{ {1}}方法。

要认识到的关键是.value()可以返回一个将自身引用为自己的add()属性的函数。这将使add的行为与add(1,2)(3)完全相同。这可以这样做:

add(1,2).add(3)
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>函数时(包括链接期间)都会重新执行属性引用和函数定义
  • 如果有人覆盖add标识符,则会导致整个实现中断:

add

这两种方法都可以通过IIFE来解决:

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
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);

答案 1 :(得分:4)

我尝试使用this进行即兴创作。适用于所有情况。

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

答案 2 :(得分:3)

因为你在ret.add函数中返回总和,这就是为什么错误即将来临尝试这样的事情,希望它能解决你的问题

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;
}

答案 3 :(得分:3)

  

此外,他们需要始终返回整数(而不是字符串),似乎有时他们会这样做,有时他们不这样做?

是的,这绝对是一个概念问题。你想要的这两件事是不兼容的。 add(2,2,2)add方法的数字或其他内容吗?

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

即使有一种奇特的方法来向nubmers添加方法,我强烈建议保持简单并始终需要“.value()”调用来结束链。这样所有对“.add”的调用都会返回一个“加法器对象”,所有对“.value”的调用都会返回一个常规数字。

  

似乎我必须继续嵌套添加函数,如果我想将两个以上链接在一起并且还为每个函数添加值函数,但显然我缺少一些简单的东西,这将允许我链接它们尽我所能,并且对其中任何一个都有价值。

答案是使用递归函数。这是一个创建我之前提到的“加法器”对象的函数:

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));
        }
    }
}

然后你的初始添加功能将如下所示:

function add(/**/){
    return mkAdder(sumArray(arguments));
}