为什么未执行的声明会降低我的功能?

时间:2012-09-12 15:05:07

标签: javascript performance

我创建了四个不同的功能,如:

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之后,我发现normalcontrol执行相同,但withArgumentswithEval的执行速度要慢得多。

为什么这些未执行的声明会对性能产生任何影响?既然它们从未被执行过,它们怎么可能产生任何影响呢?

1 个答案:

答案 0 :(得分:65)

简而言之,在函数内部调用eval并且能够访问arguments数组都在函数调用期间使用额外的设置。如果已知不会执行argumentseval,则可以跳过此额外设置。

编译器不会尝试预测是否实际访问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可以使用的新执行上下文,以便它不需要多次重新创建。


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