eval()和new Function()是同一件事吗?


89

这两个功能在后台执行相同的操作吗?(在单语句函数中)

var evaluate = function(string) {
    return eval('(' + string + ')');
}

var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

console.log(evaluate('2 + 1'));
console.log(func('2 + 1'));

3
实际上,这是在jQuery 1.7.2第573行中使用的。尽管带有“一些安全检查”
PicoCreator 2012年

2
为什么new在此讨论中考虑?Function隐式实例化一个function object。排除new根本不会更改代码。这是一个演示的jsfiddle:jsfiddle.net/PcfG8
Travis J

Answers:


114

不,他们一样。

  • eval() 在当前执行范围内将字符串评估为JavaScript表达式,并且可以访问局部变量。
  • new Function()将存储在字符串中的JavaScript代码解析为一个函数对象,然后可以调用该函数对象。它不能访问局部变量,因为代码在单独的范围内运行。

考虑以下代码:

function test1() {
    var a = 11;
    eval('(a = 22)');
    alert(a);            // alerts 22
}

如果new Function('return (a = 22);')()使用,则局部变量a将保留其值。但是,诸如Douglas Crockford之类的一些JavaScript程序员认为,除非绝对必要,否则都不应该使用两者,并且对不可信数据进行逃避/使用构造函数是不安全且不明智的。Function


10
“永远不应该使用”是这样一个宽泛的说法。但是,当任何输入超出您的控制范围时,绝对不要使用。话虽如此,除了解析JSON外,我再也不需要使用它。但是我在这里回答了看起来像是有效用法的问题,它涉及到允许管理员生成最终用户可以使用的模板功能。在这种情况下,输入是由管理员生成的,因此可以接受
Juan Mendes

3
@Juan:Crockford的观点是,评估经常被滥用(取决于您的观点),因此应将其从“最佳实践” JavaScript中排除。但是,我确实同意,当首次正确验证输入时,它对于JSON或算术表达式解析等用途很有用。甚至Crockford都在他的JSON库json2.js中使用它。
PleaseStand

那么,这是否意味着它new Function(code)eval('(function(){'+code+'})()')一个全新的执行上下文,还是一个全新的执行上下文?
乔治·莫尔

5
@GeorgeMauer:我想你的意思是eval('(function(){'+code+'})'),它将返回一个函数对象。根据MDN的说法,“使用Function构造函数创建的函数不会为其创建上下文创建闭包;它们始终在窗口上下文中运行(除非函数主体以“ use strict”;语句开头,在这种情况下,上下文未定义) 。”
PleaseStand'7

6

没有。

在您的更新中,对的调用evaluatefunc产生相同的结果。但是,他们绝对不是在幕后“做同样的事情”。该func函数创建一个新函数,然后立即执行它,而该evaluate函数只是在现场执行代码。

从最初的问题:

var evaluate = function(string) {
    return eval(string);
}
var func = function(string) {
    return (new Function( 'return (' + string + ')' )());
}

这些将给您非常不同的结果:

evaluate('0) + (4');
func('0) + (4');

无论如何,除了最例外的情况,两者都是不好的做法。
palswim 2011年

8
好吧,根据他的实现,您对自己的示例进行了捏造。如果他实施了评估,return eval('(' + string + ')');那么它们将产生相同的结果。
Juan Mendes

@Juan我没想到但是。编辑了问题
qwertymk,2011年

6

new Function创建一个可以重用的函数。eval只是执行给定的字符串并返回最后一条语句的结果。当您尝试创建一个使用Function模拟一个eval的包装函数时,您的问题被误导了。

他们在幕后共享一些代码是真的吗?是的,很有可能。完全相同的代码?不,当然。

有趣的是,这是我自己使用eval创建功能的不完善的实现。希望它能为区别带来一些启示!

function makeFunction() {
  var params = [];
  for (var i = 0; i < arguments.length -  1; i++) {
    params.push(arguments[i]);
  }
  var code = arguments[arguments.length -  1];


 // Creates the anonymous function to be returned
 // The following line doesn't work in IE
 // return eval('(function (' + params.join(',')+ '){' + code + '})');
 // This does though
 return eval('[function (' + params.join(',')+ '){' + code + '}][0]');
}

此功能与新功能之间的最大区别在于,该功能没有词法范围。因此,它将无法访问闭包变量,而我的将无法访问。


为什么不使用arguments.slice()代替for循环?或者,如果参数不是真正的数组,则[].slice.call(arguments, 1)
Xeoncross 2014年

3

只想指出此处示例中使用的一些语法及其含义:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' )());
 }

请注意,Function(...)()在结尾处带有“()”。这种语法将使func执行新函数并返回字符串,而不是返回字符串的函数,但是如果您使用以下命令:

 var func = function(string) {
     return (new Function( 'return (' + string + ')' ));
 }

现在func将返回一个返回字符串的函数。


2

如果您的意思是说,它会产生相同的结果,那么是的……但是仅仅进行评估(也就是“评估此JavaScript字符串”)会简单得多。

在下面编辑:

就像说...这两个数学问题是一样的:

1 + 1

1 +1 +1 1-1 1-1 * 1/1


他们都重新解析字符串吗?
qwertymk 2011年

它们都将解析字符串(在您的代码示例中)。不知道“重新解析”是什么意思。
蒂莫西·库里

1
我确定它们都在某个时刻解析(评估)字符串,但是当您调用函数时,Function对象可能会将字符串存储在私有成员变量中eval
palswim 2011年

1

在该示例中,结果是相同的,是的。两者都执行您传递的表达式。这就是使它们如此危险的原因。

但是他们在科学背后做了不同的事情。new Function()幕后程序涉及一个从您提供的代码中创建匿名函数的功能,该匿名函数在调用该函数时执行。

从技术上讲,传递给它的JavaScript在调用匿名函数之前不会执行。与此形成对比的是eval(),代码立即执行,并且不基于代码生成函数。


你能再讲一点吗?它们不是都在return语句中执行吗?Function()会使用除eval之外的另一堆栈调用级别吗?
qwertymk 2011年

Does the Function() one use another stack call level than eval?:我会这样假设,因为eval()不会从您的输入创建函数,而是仅执行它。new Function()创建一个新函数,然后将其调用。
克里斯·拉普兰特

@SimpleCoder:Function()不会向调用堆栈添加任何内容。仅当您调用结果函数时。eval确实做同样的事情(如果在问题中适用)
Juan Mendes
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.