我创建了四个不同的功能,如:
var normal = function() {
return;
};
var control = function() {
return;
alert("Hello, world!");
};
var withArguments = function() {
return;
arguments;
};
var withEval = function() {
return;
eval("");
};
由于他们都没有做任何事情并立即返回,我希望他们所有人都能拥有相同的速度。但是,在testing it on jsPerf之后,我发现normal
和control
执行相同,但withArguments
和withEval
的执行速度要慢得多。
为什么这些未执行的声明会对性能产生任何影响?既然它们从未被执行过,它们怎么可能产生任何影响呢?
答案 0 :(得分:65)
简而言之,在函数内部调用eval
并且能够访问arguments
数组都在函数调用期间使用额外的设置。如果已知不会执行arguments
和eval
,则可以跳过此额外设置。
编译器不会尝试预测是否实际访问arguments
数组或是否实际调用eval
,它只会检查它们是否存在于函数中。
arguments
在运行时调用使用arguments
对象的可变参数函数比不使用arguments
对象的“普通”函数更昂贵。
声明arguments
对象时绑定执行环境所需的额外步骤是specified in §10.6 of the ECMA-262 standard。创建arguments
对象是一个有点昂贵的15步过程。基本上,必须使用传入的参数填充arguments
,并且必须创建.caller
和.callee
属性。
标准说当函数进入其执行上下文时应该创建arguments
对象,除非在名为arguments
的函数内已经声明了参数,变量或函数。
出于优化的目的,大多数浏览器实际上并不创建参数对象,除非函数实际在某处使用它(即使在return
之后)。这就是为什么在引用arguments
时会看到性能损失的原因,即使从不执行包含它的行也是如此。
eval
输入eval
代码,specified in §10.4.2 of the ECMA-262 standard,需要创建一个特殊的执行上下文。基本上,它必须将调用函数的执行上下文的所有属性绑定到eval
上下文。
如果在一个函数中调用多个eval
,它们基本上都会执行两次相同的过程。为了优化,如果浏览器检测到函数中存在eval
(即使在return
之后),它会预先填充每个eval
可以使用的新执行上下文,以便它不需要多次重新创建。
请注意,这些优化是依赖于浏览器的,而且不是标准所要求的,因此某些浏览器可能实际上不会执行所描述的优化,或者它们可能采取不同的方式。