2010-11-15 73 views
26

我需要使用new Function()構造函數創建一個具有可變數量參數的函數。類似這樣的:新函數()帶有可變參數

args = ['a', 'b']; 
body = 'return(a + b);'; 

myFunc = new Function(args, body); 

是否有可能沒有eval()


非常感謝,夥計們!其實,a + b並不是我最關心的問題。我正在處理和擴展模板的代碼,我需要將未知(和可變)數量的參數傳遞給函數,以便將它們作爲局部變量引入。

例如,如果一個模板包含:

<span> =a </span> 

我需要輸出參數a的價值。也就是說,如果用戶宣佈擴大功能

var expand = tplCompile('template', a, b, c) 

,然後調用

expand(4, 2, 1) 

我需要替換=a4。是的,我很清楚功能類似於eval(),運行速度非常慢,但我沒有任何其他選擇。

+0

爲什麼不通過數組循環?無論你想要做什麼,可能有更好的方法。 – 2010-11-15 10:53:44

+0

我擔心eval無法避免,因爲您試圖將純文本評估爲代碼。 – 2010-11-15 11:02:36

+0

有更好的方法來做到這一點。如果你絕對必須使用'new Function()',那聽起來像是一項家庭作業。在這種情況下,教師正在教你如何做錯事情。 – darioo 2010-11-15 11:13:51

回答

43

可以使用apply()做到這一點:

args = ['a', 'b', 'return(a + b);']; 
myFunc = Function.apply(null, args); 

沒有new操作,Function給出完全相同的結果。您可以使用數組函數(如push(),unshift()splice())在傳遞數組之前修改數組。

你也可以通過一個逗號分隔的參數字符串功能

args = 'a, b'; 
body = 'return(a + b);'; 

myFunc = new Function(args, body); 

在一個側面說明,你知道arguments對象的?它允許您使用數組式的支架方式就可以得到傳遞給函數的所有參數:

myFunc = function() { 
    var total = 0; 

    for (var i=0; i < arguments.length; i++) 
     total += arguments[i]; 

    return total; 
} 

myFunc(a, b); 

這將是比使用功能構造更高效,並可能是實現一個更合適的方法是什麼你需要。

+0

你的建議非常完美!以下作品像魅力: args ='a,b'; body ='return(a + b);'; myFunc = new Function(args,body); – mtelis 2010-11-16 09:29:54

+0

@mtelis:偉大的東西,我很高興它幫助你:-)你可以通過點擊投票箭頭下面的複選標記來標記答案。歡迎來到Stack Overflow! – 2010-11-16 09:36:44

+0

如果你不使用new運算符,'myFunc'中的'this'變量是不是不同? – 2012-11-24 12:25:54

0

如果你只是想要一個sum(...)功能:

function sum(list) { 
    var total = 0, nums; 
    if (arguments.length === 1 && list instanceof Array) { 
     nums = list; 
    } else { 
     nums = arguments; 
    } 
    for (var i=0; i < nums.length; i++) { 
     total += nums[i]; 
    } 
    return total; 
} 

然後,

sum() === 0; 
sum(1) === 1; 
sum([1, 2]) === 3; 
sum(1, 2, 3) === 6; 
sum([-17, 93, 2, -841]) === -763; 

如果您想了解更多,請您提供更多的細節?如果你不知道你想要做什麼,那麼你很難說出你能做些什麼。以這種方式

+0

您應該添加'var nums;';否則你會使nums成爲全局的,這肯定是不希望的。 – ThiefMaster 2010-11-15 11:39:30

+0

是的。那是粗心的。謝謝你選擇了。 – 2010-11-15 12:02:46

-1
new Function(...) 

聲明函數導致 被編譯沒有的功能,和 潛在地比聲明函數的其他 方式慢。

咱們是JSLitmus檢查並運行一個小的測試腳本:

<script src="JSLitmus.js"></script> 
<script> 

JSLitmus.test("new Function ... ", function() { 
    return new Function("for(var i=0; i<100; i++) {}"); 
}); 

JSLitmus.test("function() ...", function() { 
     return (function() { for(var i=0; i<100; i++) {} }); 
}); 

</script> 

我上面所做的就是創建一個function expressionfunction constructor執行相同的操作。結果如下:

FireFox的性能結果

FireFox Performance Result

IE性能結果

IE Performance Result

基於事實,我建議使用function expression代替function constructor

var a = function() { 
var result = 0; 
for(var index=0; index < arguments.length; index++) { 
    result += arguments[index]; 
} 
return result; 
} 
alert(a(1,3)); 
+1

你可以鏈接你從句子中表達的資源嗎? – 2010-11-15 11:30:45

+13

引用是錯誤的*,有很多關於這個的神話。函數*是在您調用['Function'構造函數](http://sideshowbarker.github.com/es5-spec/#x15.3.2.1)之後編譯的。之後,它就像*任何其他用戶定義的函數*,沒有性能損失,*慢部分*可能是函數的創建,但不是函數本身創建後... – CMS 2010-11-15 14:52:56

+0

@CMS我已經更新了上述發佈並顯示一些統計信息。 – 2010-11-15 19:49:01

0

有幾種不同的方法可以編寫它。

// assign normally 
var ab = ['a','b'].join(''); 
alert(ab); 
// assign with anonymous self-evaluating function 
var cd = (function(c) {return c.join("");})(['c','d']); 
alert(cd); 
// assign with function declaration 
function efFunc(c){return c.join("");} 
var efArray = ['e','f']; 
var ef = efFunc(efArray); 
alert(ef); 
// assign with function by name 
var doFunc = function(a,b) {return window[b](a);} 
var ghArray = ['g','h']; 
var ghFunc = function(c){return c.join("");} 
var gh = doFunc(ghArray,'ghFunc'); 
alert(gh); 
// assign with Class and lookup table 
var Function_ = function(a,b) { 
    this.val = ''; 
    this.body = b.substr(0,b.indexOf('(')); 
    this.args = b.substr(b.indexOf('(')+1,b.lastIndexOf(')')-b.indexOf('(')-1); 
    switch (this.body) { 
    case "return": 
     switch (this.args) { 
     case "a + b": this.val = a.join(''); break; 
     } 
    break; 
    } 
} 
var args = ['i', 'j']; 
var body = 'return(a + b);'; 
var ij = new Function_(args, body); 
alert(ij.val); 
+0

但這只是等同於'var ab =(function(c) {...})([ '一', 'b'])'! – 2010-11-15 11:13:36

+0

@克里斯摩根,真實。我以爲他想把這些函數映射到變量上,而不是將它們放到一個函數表達式中並對其進行評估。 – WolfRevoKcats 2010-11-15 11:26:29

+0

非常感謝,夥計們!其實,a + b並不是我最關心的問題。我正在處理和擴展模板的代碼,我需要將未知(和可變)數量的參數傳遞給函數,以便將它們作爲局部變量引入。例如,如果模板包含: – mtelis 2010-11-15 21:59:45

7

@ AndyE的回答是正確的如果構造函數不關心你是否使用new關鍵字。有些功能不夠寬容。

如果你發現自己在一個場景中,你需要使用new關鍵字,你需要一個可變數量的參數發送到功能,您可以使用此

function Foo() { 
    this.numbers = [].slice.apply(arguments); 
}; 


var args = [1,2,3,4,5]; // however many you want 
var f = Object.create(Foo.prototype); 
Foo.apply(f, args); 

f.numbers;   // [1,2,3,4,5] 
f instanceof Foo; // true 
f.constructor.name; // "Foo" 

ES6及更高版本!

// yup, that easy 
 
function Foo (...numbers) { 
 
    this.numbers = numbers 
 
} 
 

 
// use Reflect.construct to call Foo constructor 
 
const f = 
 
    Reflect.construct (Foo, [1, 2, 3, 4, 5]) 
 

 
// everything else works 
 
console.log (f.numbers)   // [1,2,3,4,5] 
 
console.log (f instanceof Foo) // true 
 
console.log (f.constructor.name) // "Foo"

+0

此答案處理任意構造函數。但是,這個問題是關於'Function'構造函數的。函數構造函數根本不必與'new'一起使用,所以只需使用'Function.apply(null,args)'就足夠了。 – 2016-07-07 11:04:41

-1
function construct(){ 
     this.subFunction=function(a,b){ 
     ... 
     } 
} 
var globalVar=new construct(); 

var globalVar=new function(){ 
       this.subFunction=function(a,b){ 
       ... 
       } 
} 

我喜歡第二個版本,如果有子功能。

-1

當b繼承原型時,b.apply(null,arguments)不能正常工作,因爲'new'被省略,基礎構造函數不會被調用。

+0

這是否應該是對另一個答案的評論?這似乎與這個問題沒有任何關係。 – Bergi 2017-04-08 19:03:43

+0

這個問題特別要求'b === Function'的情況,其中'apply'確實有效(它不需要作爲構造函數調用)。有關一般情況,請參閱[這裏](http://stackoverflow.com/q/1606797/1048572)。 – Bergi 2017-04-08 19:05:04

+0

它確實是對@Andy E的評論,但是SO不會讓我直接評論 – user2969819 2017-04-09 22:17:09

-1

在此示例中,我用lodash

function _evalExp(exp, scope) { 
    const k = [null].concat(_.keys(scope)); 
    k.push('return '+exp); 
    const args = _.map(_.keys(scope), function(a) {return scope[a];}); 
    const func = new (Function.prototype.bind.apply(Function, k)); 
    return func.apply(func, args); 
} 

_evalExp('a+b+c', {a:10, b:20, c:30});