L'utilisation de fonctions anonymes affecte-t-elle les performances?

Je me demandais, existe-t-il une différence de performance entre l'utilisation de fonctions nommées et les fonctions anonymes en Javascript?

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

contre

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

Le premier est plus tidier car il n'écrase pas votre code avec des fonctions rarement utilisées, mais est-il important que vous ré-déclariez cette fonction à plusieurs reprises?

Le problème de performance ici est le coût de la création d'un nouvel objet de fonction à chaque itération de la boucle et non du fait que vous utilisez une fonction anonyme:

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

Vous créez mille objets fonctionnels distincts même s'ils possèdent le même code et pas de liaison avec la portée lexicale ( fermeture ). Ce qui suit semble plus rapide, d'autre part, car il affecte simplement la même référence de fonction aux éléments de tableau dans la boucle:

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

Si vous deviez créer une fonction anonyme avant d'entrer dans la boucle, n'ajoutez que des références aux éléments de la tablette dans la boucle, vous constaterez qu'il n'y a aucune différence de performance ou sémantique par rapport à la version de fonction nommée:

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

En bref, il n'y a aucun coût de performance observable pour l'utilisation de fonctions anonymes nommées.

Par contre, il peut sembler d'en haut qu'il n'y a pas de différence entre:

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

et:

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

Le premier est une déclaration de fonction, alors que ce dernier est une affectation variable à une fonction anonyme. Bien qu'ils semblent avoir le même effet, JavaScript les traite légèrement différemment. Pour comprendre la différence, je recommande de lire " ambiguïté de déclaration de fonction JavaScript ".

Le temps d'exécution réel pour toute approche sera en grande partie dicté par la mise en œuvre du compilateur et du temps d'exécution du navigateur. Pour une comparaison complète des performances modernes du navigateur, visitez le site JS Perf

Voici mon code de test:

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

Les resultats:
Test 1: 142 ms Test 2: 1983ms

Il semble que le moteur JS ne reconnaisse pas que c'est la même fonction dans Test2 et le compile à chaque fois.

En principe de conception générale, vous devez éviter d'imploser le même code plusieurs fois. Au lieu de cela, vous devez soulever le code commun dans une fonction et exécuter cette fonction (générale, bien testée, facile à modifier) ​​à partir de plusieurs endroits.

Si (contrairement à ce que vous déduisez de votre question), vous déclarez la fonction interne une fois et utilisez ce code une fois (et n'a rien d'autre dans votre programme) alors une fonction anormale probablement (c'est une supposition) est traitée de la même manière par la Compilateur comme fonction normal nommée.

C'est une fonctionnalité très utile dans des cas spécifiques, mais ne devrait pas être utilisé dans de nombreuses situations.

Je ne m'attendrais pas à beaucoup de différence, mais s'il y en a une, cela variera probablement selon le moteur de script ou le navigateur.

Si vous trouvez le code plus facile à écouter, la performance est sans problème à moins que vous ne vous attendiez à appeler la fonction des millions de fois.

Les objets anonymes sont plus rapides que les objets nommés. Mais appeler plus de fonctions est plus coûteux et à un degré qui éclipse les économies que vous pourriez obtenir en utilisant des fonctions anonymes. Chaque fonction appelée ajoute à la pile d'appel, ce qui introduit une quantité de frais généraux petite mais non triviale.

Mais, sauf si vous écrivez des routines de cryptage / décryptage ou quelque chose de similaire à la performance, comme d'autres l'ont noté, il est toujours préférable d'optimiser un code élégant et facile à lire sur un code rapide.

En supposant que vous écrivez un code bien conçu, les problèmes de vitesse devraient être la responsabilité de ceux qui rédigent les interprètes / compilateurs.

Là où nous pouvons avoir un impact sur la performance, c'est dans l'opération de déclarer des fonctions. Voici une référence de déclaration de fonctions dans le contexte d'une autre fonction ou à l'extérieur:

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

Dans Chrome, l'opération est plus rapide si nous déclarons la fonction à l'extérieur, mais dans Firefox c'est le contraire.

Dans un autre exemple, nous voyons que si la fonction interne n'est pas une fonction pure, elle aura également un manque de performances dans Firefox: http://jsperf.com/function-context-benchmark-3

Ce qui va certainement rendre votre boucle plus rapide dans une variété de navigateurs, en particulier les navigateurs IE, se met en boucle comme suit:

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

Vous avez mis 1000 arbitraires dans la condition de la boucle, mais vous obtenez ma dérive si vous voulez passer en revue tous les éléments du tableau.

Une référence va presque toujours être plus lente que la chose à laquelle elle se réfère. Pensez-y de cette façon – disons que vous voulez imprimer le résultat de l'ajout de 1 + 1. Ce qui a plus de sens:

 alert(1 + 1); 

ou

 a = 1; b = 1; alert(a + b); 

Je me rends compte que c'est une façon vraiment simple de l'examiner, mais c'est illustratif, n'est-ce pas? Utilisez une référence seulement si elle va être utilisée plusieurs fois – par exemple, lequel de ces exemples a plus de sens:

 $(a.button1).click(function(){alert('you clicked ' + this);}); $(a.button2).click(function(){alert('you clicked ' + this);}); 

ou

 function buttonClickHandler(){alert('you clicked ' + this);} $(a.button1).click(buttonClickHandler); $(a.button2).click(buttonClickHandler); 

La seconde est une meilleure pratique, même si elle a plus de lignes. J'espère que tout cela est utile. (Et la syntaxe jquery n'a pas éloigné personne)

@nickf

(J'aimerais que le représentant ait juste commenté, mais je viens juste de trouver ce site)

Mon point de vue est qu'il existe ici une confusion entre les fonctions nommées / anonymes et le cas d'utilisation de l'exécution + compilation en itération. Comme je l'ai illustré, la différence entre anon + named est négligeable en soi – je dis que c'est le cas d'utilisation défectueux.

Il me semble évident, mais sinon je pense que le meilleur conseil est "ne pas faire des choses stupides" (dont le changement de bloc constant + la création d'objet de ce cas d'utilisation est un) et si vous n'êtes pas sûr, testez!

OUI! Les fonctions anonymes sont plus rapides que les fonctions régulières. Peut-être que si la vitesse est de la plus haute importance … plus important que la réutilisation du code, alors envisagez d'utiliser des fonctions anonymes.

Il existe un très bon article sur l'optimisation des fonctions javascript et anonymes ici:

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

@nickf

C'est un test plutôt fatuous, vous comparez l'heure d'exécution et de compilation , ce qui va évidemment coûter la méthode 1 (compose N fois, le moteur JS en fonction) avec la méthode 2 (compile une fois). Je ne peux pas imaginer un développeur de JS qui passerait leur code d'écriture de probation de cette manière.

Une approche beaucoup plus réaliste est l'affectation anonyme, comme en fait vous utilisez pour votre méthode document.onclick ressemble davantage à ce qui suit, ce qui favorise légèrement la méthode anon.

En utilisant un cadre de test similaire à celui-ci:


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

Comme indiqué dans les commentaires à @nickf répondre: la réponse à

La création d'une fonction est-elle une fois plus rapide que la création d'un million de fois

Est simplement oui. Mais comme le montre sa performance JS, il n'est pas plus lent d'un million, ce qui montre qu'il s'accélère avec le temps.

La question la plus intéressante est:

Comment une création + course répétée se compare-t-elle pour créer une fois + course répétée.

Si une fonction effectue un calcul complexe, le temps de créer l'objet fonction est probablement négligeable. Mais qu'en est-il de la tête de création dans les cas où la course est rapide? Par exemple:

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

Cette JS Perf montre que la création de la fonction une fois est plus rapide que prévu. Cependant, même avec une opération très rapide comme une simple addition, les frais généraux de création de la fonction à plusieurs reprises ne représentent que quelques pour cent.

La différence ne devient probablement significative que dans les cas où la création de l'objet fonction est complexe, tout en conservant un temps d'exécution négligeable, par exemple, si l'ensemble du corps de la fonction est enveloppé dans un if (unlikelyCondition) { ... } .