50

我创建了四个不同的函数,如下所示:

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

由于他们都什么都不做并立即返回,我希望他们所有人都具有相同的速度。但是,在 jsPerf 上对其进行测试后,我发现它normalcontrol执行大致相同,但withArguments执行withEval速度要慢得多。

为什么这些未执行的语句会对性能产生任何影响?既然他们从来没有被执行过,他们怎么可能有任何效果呢?

4

1 回答 1

64

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

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

arguments

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

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

该标准规定,arguments对象应该在函数进入其执行上下文时创建,除非在名为 的函数中已经声明了参数、变量或函数arguments

出于优化的目的,大多数浏览器实际上不会创建 arguments 对象,除非函数在某处实际使用它(即使在 a 之后return)。这就是为什么在arguments引用时会看到性能下降的原因,即使包含它的行从未执行过。

eval

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

如果eval在一个函数中调用了多个 s,那么它们基本上都会执行两次相同的过程。为了优化,如果浏览器检测到eval函数中有一个(甚至在 a 之后return),它会预先填充这个每个人都eval可以使用的新执行上下文,这样就不需要多次重新创建它。


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

于 2012-09-12T15:15:08.727 回答