如何在函数中捕获参数数量(currying函数部分应用程序)

时间:2013-05-10 11:40:11

标签: javascript design-patterns

我正在研究限制功能和部分应用, 我正在努力改进功能schonfinkelize:

        function schonfinkelize(fn){
            var 
                slice = Array.prototype.slice,
                stored_args = slice.call(arguments, 1);
            return function(){
                var 
                    new_args = slice.call(arguments),
                    args = stored_args.concat(new_args);

                return fn.apply(null, args);
            }
        }

此函数允许传递作为参数函数作为参数传递的函数的参数的一部分(部分应用程序)所以第一次返回函数,然后再次触发函数时结果。

function add(x, y, z){
    return x + y + z;
}

var val = schonfinkelize(add, 1, 2);

console.log( val(3) ) // console output--> 6

我想在schonfinkelize里面检查函数“add”所需的参数个数(但它应该适用于每个函数)所以我可以选择何时返回另一个函数或直接选择函数“add”的结果。

如果我以这种方式使用schonfinkelize:

var val2 = schonfinkelize(add, 1, 2, 3);
console.log( val2 )    // --> function
console.log( val2() )  // --> 6

我必须触发该函数两次,而不是想要避免这种行为并在参数足够时直接定义值


可能的解决方案如下:

        function schonfinkelize(fn){
            var 
                slice = Array.prototype.slice,
                stored_args = slice.call(arguments, 1);


                //* I have added this ********
                if(fn.apply(null, stored_args))
                    return fn.apply(null, stored_args);
                //****************************

            return function(){
                var 
                    new_args = slice.call(arguments),
                    args = stored_args.concat(new_args);

                return fn.apply(null, args);
            }
        }

可能是因为如果 fn.apply(null,stored_args)返回的内容不是“null”或“NaN”,它会立即返回结果,但我认为这不是真正的高性能然后我想要参与争论。

3 个答案:

答案 0 :(得分:1)

我认为没有一种正确的方法来确定任意函数的参数数量。如果有必要,我更喜欢在函数中存储len,并检查它是否已定义,如果是,如果fn.len == stored_args.length,则返回仅返回值的函数。

function schonfinkelize(fn){
  var 
    slice = Array.prototype.slice,
    stored_args = slice.call(arguments, 1);

  if (fn.len != undefined && fn.len == stored_args.length) {
    var val = fn.apply(null, stored_args);

    return function () {
      return val;
    };
  }

  return function () {
    var 
      new_args = slice.call(arguments),
      args = stored_args.concat(new_args);

    return fn.apply(null, args);
  };
}

var f = function (a, b, c) {
  return a + b + c;
};

f.len = 3;

var g = schonfinkelize(f, 1, 2);
alert(g); // function () { var new_args = slice.call(arguments), args = stored_args.concat(new_args); return fn.apply(null, args); };
alert(g(3)); // 6

var g = schonfinkelize(f, 1, 2, 3);
alert(g); // function () { return val; };
alert(g()); // 6

答案 1 :(得分:1)

只要您要求为传递的函数定义的参数反映最终要接收的参数的实际数量,您可以使用函数的.length属性进行比较传递参数到预期的论点。

function schonfinkelize(fn) {
    if (fn.length === arguments.length - 1)
        return fn.apply(this, [].slice.call(arguments, 1));

    var 
        slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
    return function(){
        var 
            new_args = slice.call(arguments),
            args = stored_args.concat(new_args);

        return fn.apply(null, args);
    }
}

旁注...如果将.slice()缓存到新变量中,并使用fn值覆盖第一个参数,则可以避免使用this,然后使用{{1 }} ...

.call.apply()

严格模式浏览器中,您甚至可以避免使用新变量,因为参数不再映射到 if (fn.length === arguments.length - 1) { var func = fn; arguments[0] = this; return func.call.apply(func, arguments); } 中的更改。但这不适用于不支持严格模式的浏览器

答案 2 :(得分:0)

我还想提出代码的个人演变,但我要感谢斜视已解决问题,只是建议我使用属性 .length 。 在我认为允许创建一个部分函数的下一个级别,每次你想要调用它,直到你完成所有的参数,我也简化了代码:

        function schonfinkelize(fn, stored_args){
            if(fn.length == stored_args.length)
                return fn.apply(null, stored_args);

            return function(){
                var 
                    new_args = arguments[0],
                    args = stored_args.concat(new_args);

                if(fn.length == args.length)
                    return fn.apply(null, args);

                return schonfinkelize(fn, args);
            }   
        }


        function add(x, y, w, z){
            return x + y + w + z;
        }


        var 
            val  = schonfinkelize(add, [1, 2, 3, 4]),
            val2 = schonfinkelize(add, [1, 2]),
            val3 = schonfinkelize(add, [1]);


        // checking
        console.log(val);           // output --> 10 // called only 1 time
        console.log(val2([3, 4]));  // output --> 10 // called in 2 times

        val3 = val3([2]);
        val3 = val3([3]);
        console.log(val3([4]));     // output --> 10 // called 4 times!