2010-11-15 76 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});