为什么未执行的语句会减慢我的function?

我创build了四个不同的function,如下所示:

var normal = function() { return; }; var control = function() { return; alert("Hello, world!"); }; var withArguments = function() { return; arguments; }; var withEval = function() { return; eval(""); }; 

既然他们什么都不做,立即回来,我希望他们都有同样的速度。 但是, 在jsPerf上testing之后,我发现normalcontrol执行相同,但是withArguments和执行速度要慢得多。

为什么这些未执行的声明会对性能产生影响? 由于他们从来没有执行过,他们怎么可能有任何效果呢?

简而言之,在eval内部调用eval并能够访问arguments数组,都可以在函数调用期间使用额外的设置。 如果知道既不会执行arguments也不会执行eval ,这个额外的设置可以被跳过。

编译器不会试图预测arguments数组是否会被实际访问,或者eval是否被实际调用,它只会检查它们是否存在于函数中。

arguments

在运行时,调用使用arguments对象的可变参数函数比使用arguments对象的“普通”函数要昂贵。

ECMA-262标准的§10.6中规定了声明arguments对象时绑定执行环境所需的额外步骤。 创buildarguments对象是一个稍微昂贵的15个步骤的过程。 基本上, arguments必须用传入的参数填充,并且必须创build.caller.callee属性。

该标准说,当一个函数进入它的执行上下文时,应该创buildarguments对象,除非已经有一个名为arguments函数声明了参数,variables或函数。

为了优化,大多数浏览器实际上不创build参数对象,除非该函数实际上在某个地方使用(即使在return )。 这就是为什么当arguments被引用时,即使包含它的行从未被执行,你也会看到性能受到影响。

eval

按照ECMA-262标准第10.4.2节的规定,inputeval代码需要创build一个特殊的执行上下文。 基本上,它必须将调用函数的执行上下文的所有属性绑定到eval上下文。

如果在一个函数中调用了多个eval ,他们基本上都会进行两次相同的处理。 为了优化,如果浏览器检测到eval中有一个eval (即使在return ),它会预先填充每个eval可以使用的新的执行上下文,以便它不需要被重新创build多次。


请注意,这些优化是依赖于浏览器的,而不是标准所要求的 ,因此一些浏览器可能不会实际执行所描述的优化,或者它们可能会以不同的方式做事。