为什么未执行的语句会减慢我的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之后,我发现normal
和control
执行相同,但是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多次。
请注意,这些优化是依赖于浏览器的,而不是标准所要求的 ,因此一些浏览器可能不会实际执行所描述的优化,或者它们可能会以不同的方式做事。