使用匿名函数会影响性能吗?
我一直在想,在Javascript中使用命名函数和匿名函数是否有性能差异?
for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = function() { // do something }; }
VS
function myEventHandler() { // do something } for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = myEventHandler; }
第一个是整数,因为它不会使用很less使用的函数混淆你的代码,但是重要的是多次声明这个函数是否重要?
此处的性能问题是在循环的每次迭代中创build新函数对象的成本,而不是使用匿名函数的事实:
for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = function() { // do something }; }
即使它们具有相同的代码体并且没有绑定到词法作用域( 闭包 ),您也正在创build一千个不同的函数对象。 另一方面,下面看起来更快,因为它在整个循环中简单地将相同的函数引用分配给数组元素:
function myEventHandler() { // do something } for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = myEventHandler; }
如果您在进入循环之前创build了匿名函数,那么只有在循环内部将其引用赋予数组元素时,才会发现与指定的函数版本相比没有任何性能或语义差异:
var handler = function() { // do something }; for (var i = 0; i < 1000; ++i) { myObjects[i].onMyEvent = handler; }
简而言之,在命名函数上使用匿名方法没有明显的性能成本。
顺便说一下,从上面可以看出,在以下方面没有区别:
function myEventHandler() { /* ... */ }
和:
var myEventHandler = function() { /* ... */ }
前者是一个函数声明,而后者是一个匿名函数的variables赋值。 尽pipe它们可能看起来有相同的效果,但JavaScript对待它们的方式稍有不同。 为了理解差异,我build议阅读“ JavaScript函数声明歧义 ”。
任何方法的实际执行时间很大程度上取决于浏览器的编译器和运行时的实现。 有关现代浏览器性能的完整比较,请访问JS Perf站点
这是我的testing代码:
var dummyVar; function test1() { for (var i = 0; i < 1000000; ++i) { dummyVar = myFunc; } } function test2() { for (var i = 0; i < 1000000; ++i) { dummyVar = function() { var x = 0; x++; }; } } function myFunc() { var x = 0; x++; } document.onclick = function() { var start = new Date(); test1(); var mid = new Date(); test2(); var end = new Date(); alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid)); }
结果:
testing1:142mstesting2:1983ms
看起来,JS引擎不能识别它在Test2中的相同function,并且每次都编译它。
作为一个通用的devise原则,你应该避免多次使用相同的代码。 相反,您应该将常用代码提取到一个函数中,并从多个位置执行(通用的,经过良好testing的,易于修改的)函数。
如果(不像你从你的问题推断)你是一次声明的内部函数和使用该代码一次(并没有什么相同的程序)然后一个自主的函数可能 (这是一个猜测人)得到同样的待遇编译器作为正常的命名函数。
在特定情况下它是一个非常有用的function,但不应在许多情况下使用。
我不希望有太大的区别,但如果有一个,它可能会因脚本引擎或浏览器而异。
如果您发现代码更容易理解,除非您希望数百万次调用该函数,否则性能不成问题。
匿名对象比命名对象更快。 但是调用更多的函数会更加昂贵,并且在某种程度上使得使用匿名函数可能获得的任何储蓄都黯然失色。 每个被调用的函数都会添加到调用堆栈中,这会引入一个小的但不重要的额外开销。
但除非你正在编写encryption/解密例程或类似性能敏感的东西,正如许多其他人已经注意到的那样,通过优化代码来实现优雅,易于阅读的代码总是更好。
假设你正在编写体系结构良好的代码,那么速度问题应该是编写口译员/编译者的责任。
我们在哪里可以有一个性能影响是在声明函数的操作。 这是一个在另一个函数或外部的上下文中声明函数的基准:
http://jsperf.com/function-context-benchmark
在Chrome中,如果我们在外部声明函数,操作速度会更快,但在Firefox中则相反。
在另一个例子中,我们看到如果内部函数不是一个纯函数,那么在Firefox中也会缺乏性能: http : //jsperf.com/function-context-benchmark-3
什么肯定会让你的循环在各种浏览器,尤其是IE浏览器,更快循环如下:
for (var i = 0, iLength = imgs.length; i < iLength; i++) { // do something }
你已经在循环条件中放入了一个任意的1000,但是如果你想要遍历数组中的所有条目,那么你就会发生漂移。
一个引用几乎总是会比它引用的东西慢。 想想这样 – 假设你想打印添加1 + 1的结果。更有意义的是:
alert(1 + 1);
要么
a = 1; b = 1; alert(a + b);
我意识到这是一个非常简单的方法来看待它,但它是说明性的,对吗? 只有在多次使用时才使用引用 – 例如,这些示例中的哪一个更有意义:
$(a.button1).click(function(){alert('you clicked ' + this);}); $(a.button2).click(function(){alert('you clicked ' + this);});
要么
function buttonClickHandler(){alert('you clicked ' + this);} $(a.button1).click(buttonClickHandler); $(a.button2).click(buttonClickHandler);
第二个是更好的做法,即使它有更多的线路。 希望这一切都有帮助。 (和jQuery的语法并没有抛出任何人)
@nickf
(希望我有代表只是评论,但我只是刚刚发现这个网站)
我的观点是,在命名/匿名函数和在迭代中执行+编译的用例之间存在混淆。 正如我所说,anon + named之间的区别本身是微不足道的 – 我说这是用例是错误的。
对我来说似乎是很明显的,但如果不是,我认为最好的build议是“不要做愚蠢的事情”(其中不断的块转移+对象创build这个用例是一个),如果你不确定,testing!
是! 匿名函数比常规函数更快。 也许如果速度是最重要的…比代码重用更重要,那么就考虑使用匿名函数。
这里有一篇关于优化javascript和匿名函数的非常好的文章:
http://dev.opera.com/articles/view/efficient-javascript/?page=2
@nickf
这是一个相当愚蠢的testing,你正在比较执行和编译时间,这显然是要花费方法1(编译N次,JS引擎依赖)与方法2(编译一次)。 我无法想象一个JS开发者会以这种方式通过他们的试用编程代码。
一个更现实的方法是匿名分配,因为实际上你正在使用你的document.onclick方法更像是下面这个,实际上它有利于anon方法。
使用类似的testing框架给你:
function test(m) { for (var i = 0; i < 1000000; ++i) { m(); } } function named() {var x = 0; x++;} var test1 = named; var test2 = function() {var x = 0; x++;} document.onclick = function() { var start = new Date(); test(test1); var mid = new Date(); test(test2); var end = new Date(); alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms"); }
正如在评论中指出的@nickf答案:答案是
创造一个function比创造一百万次更快
简直是的。 但正如他的JS所显示的那样,它不会慢一百万倍,表明它随着时间的推移实际上变得更快。
对我来说更有趣的问题是:
如何重复创build+运行比较创build一次+重复运行 。
如果函数执行复杂的计算,则创build函数对象的时间很可能可以忽略不计。 但是如果运行速度很快,那么创造的头脑又是如何呢? 例如:
// Variant 1: create once function adder(a, b) { return a + b; } for (var i = 0; i < 100000; ++i) { var x = adder(412, 123); } // Variant 2: repeated creation via function statement for (var i = 0; i < 100000; ++i) { function adder(a, b) { return a + b; } var x = adder(412, 123); } // Variant 3: repeated creation via function expression for (var i = 0; i < 100000; ++i) { var x = (function(a, b) { return a + b; })(412, 123); }
这个JS Perf表明,只需要一次创build函数就可以达到预期的效果。 但是,即使像一个简单的添加一样快速的操作,反复创build函数的开销也只有百分之几。
在创build函数对象比较复杂的情况下,如果整个函数体被包装进一个if (unlikelyCondition) { ... }
这个差别可能只会变得很重要。