2011-09-06 29 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