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

IT技术 javascript
2021-02-14 02:13:10

我正在尝试编写一个可以在许多场景中使用的 add 函数。

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

如果我想将两个以上的函数链接在一起并将值函数添加到它们中的每一个,似乎我将不得不继续嵌套添加函数,但显然我错过了一些简单的东西,它可以让我尽可能多地链接它们我喜欢并称其为其中任何一个的value。

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

4个回答

看看您arguments在两个不同地方以类似方式使用的方式,很明显您正在复制功能,这就是您遇到必须“无限嵌套”该.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标识符,则会导致整个实现中断:

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

这两种情况都可以通过 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>

sum.toString.bind(sum);? 聪明的。我想知道为什么你没有对valueOf……做同样的事情
2021-05-05 02:13:10
@Bergi 因为它需要一个不必要的装箱操作,但这也许只是不必要的优化,并且sum.toString.bind(sum)还涉及不必要的装箱。
2021-05-05 02:13:10

我试图即兴使用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-小提琴

由于您在 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;
}
add这里方法的实现引入了不一致,因为它有副作用:jsfiddle.net/hjrj7mnq/1
2021-05-01 02:13:10

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

是的,这绝对是一个概念问题。你想要的这两件事不兼容。add(2,2,2)数字还是带有add方法的东西

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

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

如果我想将两个以上的函数链接在一起并将值函数添加到每个函数中,似乎我将不得不继续嵌套添加函数,但显然我错过了一些简单的东西,可以让我尽可能多地链接它们我喜欢并称其为其中任何一个的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));
}