it-swarm.cn

使用匿名函数会影响性能吗?

我一直想知道,在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;
}

第一个是更整洁,因为它不会使用很少使用的函数使代码混乱,但重要的是你多次重新声明该函数是否重要?

81
nickf

这里的性能问题是在循环的每次迭代中创建新函数对象的成本,而不是使用匿名函数的事实:

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

您正在创建一千个不同的函数对象,即使它们具有相同的代码体并且没有绑定到词法范围( closure )。另一方面,以下似乎更快,因为它只是在整个循环中为数组元素分配 same function引用:

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

如果您在进入循环之前创建匿名函数,那么只在循环内部将对它的引用分配给数组元素时,您会发现与命名函数版本相比,没有任何性能或语义差异:

var handler = function() {
    // do something    
};
for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = handler;
}

简而言之,使用匿名命名函数没有可观察到的性能成本。

顺便说一下,从上面看,两者之间没有区别:

function myEventHandler() { /* ... */ }

和:

var myEventHandler = function() { /* ... */ }

前者是 函数声明 而后者是匿名函数的变量赋值。尽管它们似乎具有相同的效果,但JavaScript确实对它们的处理方式略有不同。为了理解差异,我建议阅读,“ JavaScript函数声明歧义 ”。

任何方法的实际执行时间很大程度上取决于浏览器的编译器和运行时的实现。有关现代浏览器性能的完整比较,请访问 JS Perf站点

81
Atif Aziz

这是我的测试代码:

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));
}

结果:
测试1:142ms测试2:1983ms

似乎JS引擎无法识别它在Test2中的功能是相同的,并且每次都编译它。

21
nickf

作为一般设计原则,您应该避免多次使用相同的代码。相反,你应该将公共代码提升到一个函数中,并从多个位置执行(通用的,经过良好测试,易于修改)的函数。

如果(不像你从你的问题推断的那样)你宣布内部函数一次并且使用该代码一次(并且在程序中没有其他任何内容)那么一个自治函数 可能 (那就是猜测伙伴)被处理了编译器作为普通命名函数的方式相同。

它在特定情况下非常有用,但不应在许多情况下使用。

2
Tom Leys

在我们可以产生性能影响的地方是声明功能的操作。这是在另一个函数或外部的上下文中声明函数的基准:

http://jsperf.com/function-context-benchmark

在Chrome中,如果我们在外部声明该功能,操作会更快,但在Firefox中则相反。

在其他示例中,我们看到如果内部函数不是纯函数,它在Firefox中也会缺乏性能: http://jsperf.com/function-context-benchmark-3

1
Pablo Estornut

我不希望有太大差异,但如果有一个,它可能会因脚本引擎或浏览器而异。

如果您发现代码更易于理解,则性能不是问题,除非您希望将该函数调用数百万次。

1
Joe Skora

匿名对象比命名对象更快。但是调用更多函数会更加昂贵,并且在某种程度上会使您使用匿名函数所获得的任何节省都黯然失色。每个被调用的函数都会添加到调用堆栈中,这会引入一些小但非常重要的开销。

但除非您正在编写加密/解密例程或类似的对性能敏感的事情,否则许多其他人已经注意到,优化优雅,易于阅读的代码优于快速代码。

假设您正在编写设计良好的代码,那么速度问题应由编写解释器/编译器的人负责。

1
pcorcoran

@nickf

这是一个相当愚蠢的测试,你比较执行 和编译 时间显然要花费方法1(编译N次,JS引擎依赖)与方法2(编译一次)。我无法想象一个JS开发人员会以这种方式传递他们的缓刑编写代码。

一个更现实的方法是匿名赋值,因为事实上你正在使用你的document.onclick方法更像是以下,实际上温和地赞成anon方法。

使用与您类似的测试框架:


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");
}
0
annakata

@nickf

(希望我让代表只是评论,但我只是发现了这个网站)

我的观点是,命名/匿名函数与在迭代中执行+编译的用例之间存在混淆。正如我所说,anon + named之间的区别本身可以忽略不计 - 我说它是用例有缺陷的。

这对我来说似乎很明显,但如果不是,我认为最好的建议是“不要做蠢事”(其中常量块移位+此用例的对象创建是一个),如果你不确定,请测试!

0
annakata

是!匿名函数比常规函数更快。也许如果速度是最重要的......比重用代码更重要的是考虑使用匿名函数。

这里有一篇关于优化javascript和匿名函数的文章:

http://dev.opera.com/articles/view/efficient-javascript/?page=2

0
Christopher Tokar

一个引用几乎总是比它所引用的东西慢。可以这样想吧 - 假设您要打印添加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语法没有抛弃任何人)

0
matt lohkamp

正如对@nickf的评论所指出的那样:答案

创建一个函数的速度比创建它的速度快一百万倍

简直是的。但正如他的JS表示,它并没有减慢百万分之一,表明它实际上随着时间的推移变得更快。

对我来说更有趣的问题是:

如何重复 创建+运行 比较以创建一次+重复 运行

如果函数执行复杂计算,则创建函数对象的时间很可能是微不足道的。但是,在 run 快速的情况下, create 的首要问题是什么?例如:

// 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 表明只需按一次创建函数就会更快。但是,即使像简单的添加一样快速操作,重复创建函数的开销也只有几个百分点。

差异可能仅在创建函数对象复杂的情况下变得显着,同时保持可忽略的运行时间,例如,如果整个函数体被包装到if (unlikelyCondition) { ... }中。

0
bluenote10

什么肯定会使你的循环在各种浏览器中更快,特别是IE浏览器,循环如下:

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

你已经在循环条件中输入了任意1000,但是如果你想要遍历数组中的所有项目,你会得到我的漂移。

0
Sarhanis