为什么 JavaScript 的 Eval 函数会比它不应该更慢?

4

我想要创建自己的JS框架,只为了自己的乐趣(也许以后能转化为实用),而且我大部分时间都致力于动态生成JS(因为把JS作为中间语言来使用不是一个好主意,我个人认为)。

但是我遇到了一个问题。我试图做以下操作:

var f = null;
var converted = "f = function(){ <do something here>";
for(var x in list)
 converted+=list[x];
converted+="};";
eval(converted);

所以这基本上将构造函数写入f,我稍后可以执行它。但问题在于 - 它运行得比我完全在文件中编写f的代码要慢,这非常奇怪。让我解释一下为什么对我来说这很奇怪:当您每次运行eval时,Chrome预编译器(或任何浏览器预编译器 - Chrome是我针对的)无法缓存编译代码,因为它期望它会随着每次运行而更改。
然而,当您将其保存到函数中时,它会创建一个新的VM机器文件,因为在保存了函数的代码之后,您无法更改它。那么,为什么当我执行这样的eval时,它比正常执行要慢呢?我的意思是在代码被执行超过一次之后。为什么这很重要?
PS:我上面展示的方法比每次进行评估都要快。所以我不明白为什么秒表显示介于其中的时间,再次忽略第1-10个调用。
PPS:测试案例:http://jsperf.com/evaluated-function-vs-real-function/2

1
相信我,就是这样。当你评估变量“converted”时,f=function()...被执行,它将新函数写入变量f中。 - Andrew Yukhymchak
1
当我测试它时,我发现使用eval创建的函数和常规函数之间没有性能差异:http://jsperf.com/evaluated-function-vs-real-function - Guffa
啊,没事了 - 我没有看到 f 在你的 eval 代码之外被引用。 - Sam Dufel
1
在这里使用函数构造器可能会有所帮助。严肃地说,以这种方式使用eval非常危险,被认为是有害的。至少你应该将函数值分配为eval调用的结果,即, var converted = "var f = function() { ..."; ... converted += "}; f;" var f = eval(converted); 但我建议使用 var f = new Function(converted); 其中converted仅为您的函数主体。 - sjrd
@AndrewYukhymchak,你有没有考虑调用“eval”函数的开销?你应该问的问题是“Eval是否应该更快?” - Pacerier
显示剩余6条评论
1个回答

5

eval基本上是无法优化的,因为编译器不知道它在做什么。即使你将其保存到一个函数中,编译器也必须放弃很多优化,因为以某种方式改变你的代码可能会破坏eval函数。

这就是为什么通常情况下,当你需要执行eval时,你会在另一个函数中执行:这样,编译器可以确定你没有修改eval中的局部作用域,并且进行更好的优化。

JS VMs实际上非常依赖启发式算法。它们试图猜测您想要做什么,并针对一般情况进行优化。eval(或new Function)阻止了它们做很多事情。

Function.new可能会更快一些,因为编译器会知道它不会尝试修改作用域。


此外!请注意,eval可能与您习惯的有所不同。例如,eval('a')(0, eval)('a')相同

我将用这个来演示

window.a = 5;
void function () {
  var a = 1;
  eval('a = 2');
  console.log(a);
  console.log(window.a);
}();

这将输出1,然后是5
window.a = 5;
void function () {
  var a = 1;
  (0,eval)('a = 2'); // <- this line has changed
  console.log(a);
  console.log(window.a);
}();

这将打印出12

您也可以阅读此文:全局eval,有哪些选项


那么如果我使用new Function('<CODE>'),我可以得到更好的结果吗? - Andrew Yukhymchak
奇怪的是,new Function 比较慢:http://jsperf.com/evaluated-function-vs-real-function/2 - Andrew Yukhymchak
是的,我也曾经见过这种情况。但那可能与虚拟机有关。 - Ven
3
关于全局 eval:http://perfectionkills.com/global-eval-what-are-the-options/ - Felix Kling
第一段代码片段应该输出 2 5,因为直接调用 eval 是在调用者的作用域中运行的。 - Thomson
显示剩余2条评论

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