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

IT技术 javascript performance optimization
2021-01-14 06:59:56

我一直在想,在 Javascript 中使用命名函数和匿名函数之间是否存在性能差异?

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

对比

function myEventHandler() {
    // do something
}

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

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

6个回答

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

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

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

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 站点

似乎基准测试结果非常依赖于 js-engine!
2021-04-01 06:59:56
JS Perf 示例中是不是有缺陷:案例 1 只定义了函数,而案例 2 和 3 似乎不小心调用了函数。
2021-04-04 06:59:56
您忘记了函数体前的括号。我刚刚测试了它,它们是必需的。
2021-04-10 06:59:56
那么使用这个推理,是否意味着在开发node.jsWeb 应用程序时,最好在请求流之外创建函数,并将它们作为回调传递,而不是创建匿名回调?
2021-04-10 06:59:56

这是我的测试代码:

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中的相同功能并每次都编译它。

你的答案值得更多重视。我在 Intel i5 4570S 上的时间:Chrome 41 (1/9)、IE11 (1/25)、FF36 (1/14)。显然,循环中的匿名函数性能更差。
2021-03-26 06:59:56
这个测试是在哪个浏览器中进行的?
2021-03-27 06:59:56
这个测试并不像看起来那么有用。在这两个例子中,内部函数实际上都没有被执行。这个测试实际上表明,创建一个函数 10000000 次比创建一个函数要快。
2021-04-01 06:59:56
我在 Chrome 23 上的时间:(2ms/17ms),IE9:(20ms/83ms),FF 17:(2ms/96ms)
2021-04-06 06:59:56
此测试表明引擎无法识别新创建的函数始终相同。但在 2021 年差异较小。
2021-04-11 06:59:56

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

如果(与您从问题中推断出的内容不同)您声明了一次内部函数并使用了一次该代码(并且您的程序中没有其他任何相同的内容),那么匿名函数可能(这是猜测)被以相同的方式处理编译器作为一个普通的命名函数。

它在特定情况下是一个非常有用的功能,但不应该在许多情况下使用。

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

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

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

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

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

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