eval() 和 new Function() 是一样的吗?

110

这两个函数在幕后做的事情是一样的吗?(单语句函数方面)

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
2
为什么在这个讨论中要考虑 newFunction 隐式实例化了一个 function object。不使用 new 不会改变代码。这里有一个 jsfiddle 演示:http://jsfiddle.net/PcfG8/ - Travis J
6个回答

143

不,它们不是相同的。

  • eval() 将一个字符串作为JavaScript表达式进行求值,并可以访问本地变量。
  • new Function()将存储在一个字符串中的JavaScript代码解析为函数对象,然后可以调用该函数。由于代码在单独的作用域中运行,因此无法访问本地变量。

考虑以下代码:

function test1() {
    var a = 11;
    eval('(a = 22)');
    alert(a);            // alerts 22
}
如果使用new Function('return (a = 22);')(),本地变量a将保留其值。然而,一些JavaScript程序员,例如Douglas Crockford认为,除非绝对必要,否则不应使用任何一个方法,并且在不受信任的数据上进行eval/使用Function构造函数是不安全和不明智的。(参考链接1)(参考链接2)

18
“should never be used”这样的说法太过笼统。怎么样,可以说当任何输入不受控制时就不应该使用。话虽如此,除了解析JSON之外,我从未需要使用它。但是我在这里回答过一些看起来是合理的问题,其中涉及允许管理员生成可供最终用户使用的模板函数。在这种情况下,输入是由管理员生成的,因此是可以接受的。 - Ruan Mendes
4
Crockford认为eval经常被误用(这取决于你的观点),因此应该从“最佳实践”JavaScript中排除。不过,我同意它在JSON或算术表达式解析等方面是有用的,前提是输入首先经过适当验证。即使是Crockford在他的JSON库json2.js中也使用了它。 - PleaseStand
1
那么这是否意味着 new Function(code)eval('(function(){'+code+'})()') 是相同的,还是它是一个全新的执行上下文? - George Mauer
8
@GeorgeMauer: 我想你的意思是 eval('(function(){'+code+'})') ,这将返回一个函数对象。根据 MDN 的说明,“使用 Function 构造函数创建的函数不会创建闭包,它们总是在 window 上下文中运行(除非函数体以"use strict"语句开头,在这种情况下上下文是 undefined)。” - PleaseStand

7

不。

在您的更新中,对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
11
你的例子与他的实现不符。如果他的实现是return eval('(' + string + ')');,那么它们会产生相同的结果。 - Ruan Mendes
@Juan 我没有想到,但是是的。我编辑了这个问题。 - qwertymk

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

与 new Function 不同的最大区别在于 Function 没有词法作用域,因此它不能访问闭包变量,而我的函数可以。


1
为什么不使用arguments.slice()而是用for循环呢?或者如果arguments不是一个真正的数组,可以使用[].slice.call(arguments, 1) - Xeoncross

3

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

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

请注意,Function(...)()的末尾有“()”。这种语法会导致func执行新函数并返回字符串,而不是返回一个返回字符串的函数。但是如果您使用以下内容:
 var func = function(string) {
     return (new Function( 'return (' + string + ')' ));
 }

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


2

如果你的意思是,它是否会产生相同的结果,那么是的...但只需使用eval(也就是“计算这个JavaScript字符串”)会更简单。

以下是编辑后的内容:

这就像在问... 这两个数学问题是否相同:

1 + 1

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


它们都重新解析字符串吗? - qwertymk
它们都将解析该字符串(在您的代码示例中)。不知道您所说的“重新解析”是什么意思。 - Timothy Khouri
1
我确定它们两者在某个时刻都会解析(评估)字符串,但 Function 对象可能会将字符串存储在私有成员变量中,在调用函数时进行 eval - palswim

1
在这个例子中,结果是一样的。它们都执行你传递的表达式。这就是使它们如此危险的原因。
但它们在幕后做了不同的事情。涉及到 new Function() 的那个,在幕后从提供的代码创建一个匿名函数,并在调用函数时执行。你传递给它的 JavaScript 技术上直到你调用匿名函数才会被执行。这与 eval() 不同,后者会立即执行代码,而不会基于它生成一个函数。

你能再详细解释一下吗?它们不都在return语句中执行吗?Function()使用的堆栈调用级别是否与eval不同? - qwertymk
“Function()” 和 “eval()” 是否使用不同的堆栈调用层级呢?我认为是这样的,因为“eval()”不会从您的输入创建一个函数-它只是执行它。“new Function()” 创建一个新函数,然后调用它。 - Chris Laplante
@SimpleCoder:Function() 不会向调用栈中添加任何内容。只有在调用结果函数时才会添加。eval 在相同的情况下也会执行完全相同的操作。 - Ruan Mendes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接