如何以编程方式设置函数的长度

时间:2011-09-06 08:01:13

标签: javascript

length property of functions告诉'预期'参数列表有多长:

console.log((function () {}).length);  /* 0 */
console.log((function (a) {}).length); /* 1 */
console.log((function (a, b) {}).length); /* 2 etc. */

然而,这是一种只读方法:

f = function (a) {};
alert(f.length); // 1
f.length = 3;
alert(f.length); // 1

有没有办法以编程方式设置该长度?到目前为止我最接近的是使用Function构造函数:

f = new Function("a,b,c", "/* function body here */");
f.length; // 3

然而,使用Function与eval基本相同,我们都知道它有多糟糕。我还有其他选择吗?

5 个答案:

答案 0 :(得分:6)

事实证明函数的Is there any built in setting that needs to be changed or do we have to use some external package.属性为configurable,这意味着您可以使用.defineProperty更改函数的长度值。例如:

length

这适用于最新版本的Chrome和Firefox,但在Safari(v9.1.1)中无效。

答案 1 :(得分:5)

目前,这是我能想到的最佳解决方案。

makeFunc = function (length, fn) {
    switch (length) {
    case 0 : return function () { return fn.apply(this, arguments); };
    case 1 : return function (a) { return fn.apply(this, arguments); };
    case 2 : return function (a,b) { return fn.apply(this, arguments); };
    case 3 : return function (a,b,c) { return fn.apply(this, arguments); };
    case 4 : return function (a,b,c,d) { return fn.apply(this, arguments); };
    case 5 : return function (a,b,c,d,e) { return fn.apply(this, arguments); };
    case 6 : return function (a,b,c,d,e,f) { return fn.apply(this, arguments); };
    case 7 : return function (a,b,c,d,e,f,g) { return fn.apply(this, arguments); };
    case 8 : return function (a,b,c,d,e,f,g,h) { return fn.apply(this, arguments); };
    case 9 : return function (a,b,c,d,e,f,g,h,i) { return fn.apply(this, arguments); };
    default : return function (a,b,c,d,e,f,g,h,i,j) { return fn.apply(this, arguments); };
    }
};

使用示例:

var realFn = function () {
    return "blah";
};

lengthSix = makeFunc(6, realFn);

lengthSix.length; // 6
lengthSix(); // "blah"

就个人而言,每当我在编程时使用复制和粘贴时,我总是畏缩,所以我很高兴听到任何更好的选择。

<强>更新

我想到了一种可以适用于任意大小的方法,与上面的例子不同,后者受到复制和粘贴多少次的限制。本质上,它动态创建一个函数(使用new Function),它将返回一个正确大小的函数,然后只是代理通过它传递给它的任何函数。是的,这确实伤到了你的头脑。不管怎么说,我以为我会根据上述情况对它进行基准测试......

http://jsperf.com/functions-with-custom-length(你也可以在那里看到'邪恶'代码。)

邪恶的方法比hacky copypasta方法慢几百倍,所以你去吧。

答案 2 :(得分:3)

我正在做的事情就像你要求大致使用以下内容:

/* Make a new function with a given size */
function SizedFunc(num_args) {
  if(SizedFunc.sizedFuncs === undefined) SizedFunc.sizedFuncs = {};
  if(SizedFunc.sizedFuncs[num_args] === undefined) {
    var argNames = [];
    for(var i = 0; i < num_args; ++i) {
      argNames.push('arg' + i);
    }
    SizedFunc.sizedFuncs[num_args] = new Function(argNames, 'return this.apply(null, arguments);');
  }
  return SizedFunc.sizedFuncs[num_args];
}

这个 使用一个Function构造函数,但是只有每个函数大小才能使用一个函数构造函数来创建一个函数包装器(缓存该大小)之后我使用wrapper.bind( real_function)提供实现作为函数表达式/对象。

优点是支持任何大小,我们不是硬编码函数定义,但实际的函数实现永远不会以'eval'方式完成,传递给Function构造函数的字符串总是相同的。

/* ---- example ---- */ 
var a = SizedFunc(4).bind(function() { console.log.apply(null, arguments); });
var b = SizedFunc(4).bind(function(a, b, c, d) { console.log(a + b, c + d); });

console.log(typeof a);  // -> function
console.log(typeof b);  // -> function
console.log(a.length);  // -> 4
console.log(b.length);  // -> 4
a(1, 2, 3, 4)           // -> 1 2 3 4
a(1, 2, 3, 4, 5);       // -> 1 2 3 4 5
b(1, 2, 3, 4)           // -> 3 7

我确信有很多原因这也很糟糕(从使用绑定意义开始,这种方式创建的函数不能绑定到对象)但它在需要能够的情况下很有用动态创建任意长度的函数。

答案 3 :(得分:1)

根据第103页的ECMA Script standard, revision 5.1.length对象上的Function参数不可写,因此在声明函数时设置它并且不可更改(如果按照规范实现) )。

因此,根据需要创建具有特定.length的函数的唯一方法是要么具有各种长度的函数(如nickf建议的那样),创建一个Function对象(就像你一样)已经提到过)或者使用动态创建的字符串eval()。我不知道你真正试图解决什么问题,但我个人发现使用eval()如果你知道你正在使用它的代码的来源并且有办法检查它或知道它将会或不会具有什么。在这种情况下,在调用eval()之前以编程方式在字符串中生成一定数量的参数不会产生我所知道的安全风险。

如果这是你自己的所有代码,你可以在函数.dynLength上创建一个可变的新属性,并将其设置为你想要的任何东西并让你的代码使用它。

答案 4 :(得分:1)

npm 模块 util-arity 可以满足您的需求:

const arity = require('util-arity');

arity(3, () => {}).length;  //» 3