2011-09-06 68 views
8

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 

是否有以编程方式设置该长度的方法?到目前为止,我来最接近的是使用功能的构造函数:

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

但是,使用功能是基本相同eval,我们都知道这是多么糟糕。我还有什么其他选择?

+0

只是有趣的是,什么原因让你甚至想到这个问题? – shabunc

+2

你为什么要设置长度?你知道当你实际调用一个函数时,你可以传递比“期望”参数更少的参数,或者更多? (如果你传递更多的函数可以通过它的'arguments'对象访问它们。) – nnnnnn

+0

出于兴趣,你为什么要这样做? – Russell

回答

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方法慢数百倍,所以你去了。因此,当函数声明它被设置,而不是多变(如果按规格实现)

+0

这是我们需要JavaScript中的宏的原因之一。 –

1

按照ECMA Script standard, revision 5.1 103页上,一个Function对象上的.length参数是不可写的。因此,根据需求创建具有特定.length函数的唯一方法是要么有一堆函数位于不同长度(如nickf所示),创建一个Function对象(如您已经提到的)或使用eval()与动态创建的字符串。我不知道你真的想要解决什么问题,但是我个人发现使用eval()是没有问题的,如果你知道你使用的代码的来源并且有办法检查它或者知道它将会或不会有什么。在这种情况下,在调用eval()之前以编程方式在字符串中生成一定数量的参数并不会带来我所知的安全风险。

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

+0

非常感谢,但是使用'Function'或'eval'的* real *问题是执行该操作的范围。这不等同于在当前范围内编写该代码,这在我的情况下不起作用。 – nickf

+1

@nickf - 你试图解决的真正问题是什么?为什么你需要动态创建一个具有特定'.length'值的函数? – jfriend00

+0

它是一个包装任意函数来执行诸如存根和间谍(例如:统计调用次数,存储参数等)的库。由于使用它的代码*可能会检查函数的长度属性,所以我想确保它在包装后保持不变。 – nickf

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

使用函数构造函数,但在严格限制的方式,只有永远每一次大小功能创建一个函数包装器(为此大小缓存)之后,我使用wrapper.bind(real_function)将实现作为函数表达式/对象提供。

好处是支持任何大小,我们不是硬编码函数定义,但实际函数实现决不会像'eval'一样完成,并且传递给函数构造函数的字符串总是相同的。

/* ---- 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 

我敢肯定有很多的原因,这是不好的,太(开始使用这种方式创建不能被绑定到一个对象绑定含意的功能),但它是在情况下非常有用,其中一个需要能够动态地创建任意长度的函数。

1

有一个NPM模块util-arity这你想要做什么:

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

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

原来,length财产上的功能是configurable,这意味着你可以使用.defineProperty上改变长度的价值一个函数。例如:

function hi() {} 
hi.length === 0; // Expected 

Object.defineProperty(hi, "length", { value: 5 }) 
hi.length === 5; // Intriguing 

这适用于最新版本的Chrome和Firefox,但它在Safari(v9.1.1)中不起作用。

+0

这也适用于现代V8(我使用节点6.x测试过)。谢谢! – doublerebel