2008-09-17 4 views
72

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

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

Le premier est plus net car il n'encombre pas votre code avec des fonctions rarement utilisées, mais est-il important que vous re-déclarer cette fonction à plusieurs reprises?

+0

Je sais que ce n'est pas dans la question, mais en ce qui concerne la propreté du code/la lisibilité, je pense que la «bonne façon» est quelque part au milieu. "Clutter" des fonctions de premier niveau rarement utilisées est ennuyeux, mais le code fortement imbriqué dépend beaucoup des fonctions anonymes qui sont déclarées en ligne avec leur invocation (pensez à node.js callback hell). Le premier et le second peuvent rendre le traçage de débogage/exécution difficile. –

+0

Les tests de performances ci-dessous exécutent la fonction pour des milliers d'itérations. Même si vous voyez une différence substantielle, une majorité des cas d'utilisation ne le feront pas dans les itérations de cet ordre. Par conséquent, il est préférable de choisir ce qui convient à vos besoins et ignorer les performances pour ce cas particulier. – Medorator

+0

@nickf bien sûr sa trop vieille question, mais voir la nouvelle réponse mise à jour –

Répondre

74

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

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

Vous créez un millier d'objets fonction distincte, même si elles ont le même corps de code et aucune liaison à la portée lexicale (closure). Ce qui suit semble plus rapide, d'autre part, parce qu'elle affecte simplement la même référence de fonction aux éléments du tableau tout au long de la boucle:

function myEventHandler() { 
    // do something 
} 

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

Si vous deviez créer la fonction anonyme avant d'entrer dans la boucle, alors seulement attribuer des références à ce document aux éléments du tableau tandis que l'intérieur de la boucle, vous constaterez qu'il n'y a pas de performance ou la différence sémantique que ce soit 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 pas de frais de performance observable en utilisant des fonctions anonymes sur-nommées.

En aparté, il peut apparaître 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 celle-ci est une variable affectation à une fonction anonyme. Bien qu'ils puissent sembler avoir le même effet, JavaScript les traite légèrement différemment. Pour comprendre la différence, je recommande de lire "JavaScript function declaration ambiguity".

La durée d'exécution réelle de toute approche dépend en grande partie de l'implémentation du compilateur et de l'environnement d'exécution par le navigateur. Pour une comparaison complète des performances du navigateur moderne, visitez the JS Perf site

+0

Vous avez oublié les parenthèses avant le corps de la fonction. Je viens de le tester, ils sont obligatoires. –

+0

@chinoto Merci de souligner l'omission. Fixé! –

+0

il semble que les résultats de référence sont très dépendants du moteur js! – aleclofabbro

2

En principe, vous devez éviter d'implémenter plusieurs fois le même code. Au lieu de cela, vous devez lever 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 en utilisant ce code une fois (et avoir rien d'autre identique dans votre programme), puis une fonction anonomous probablement (thats un des gens de deviner) obtient traité de la même manière par le compilateur comme une fonction nommée normale.

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

1

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

Si vous trouvez que le code est plus facile à graver, les performances ne posent aucun problème, sauf si vous prévoyez d'appeler la fonction des millions de fois.

0

Ce qui va certainement faire votre boucle plus rapide à travers une variété de navigateurs, en particulier les navigateurs IE, est mise en boucle comme suit:

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

Vous avez mis dans un 1000 arbitraire dans la condition de la boucle, mais vous obtenez mon dérive si vous voulez parcourir tous les éléments du tableau.

20

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 résultats:
Test 1: 142ms Test 2: 1983ms

Il semble que le moteur JS ne reconnaît pas que ce soit la même fonction Test2 et le compile à chaque fois.

+3

Dans quel navigateur a été effectué ce test? – andnil

+5

Temps pour moi sur Chrome 23: (2ms/17ms), IE9: (20ms/83ms), FF 17: (2ms/96ms) – Davy8

+0

Votre réponse mérite plus de poids. Mes temps sur Intel i5 4570S: Chrome 41 (1/9), IE11 (1/25), FF36 (1/14). Clairement, la fonction anonyme dans une boucle est pire. – ThisClark

0

@nickf

C'est un peu sotte épreuve cependant, vous comparez l'exécution et la méthode de compilation temps là qui va évidemment coûter 1 (N fois, compile moteur JS) selon la méthode 2 (compile une fois). Je ne peux pas imaginer un développeur JS qui passerait leur code d'écriture de probation de telle manière.

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

L'utilisation d'un framework de test semblable à la vôtre:


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

une référence sera presque toujours plus lente que la chose à laquelle il fait référence. Pensez-y de cette façon - disons que vous voulez imprimer le résultat de l'addition 1 + 1. Ce qui est plus logique:

alert(1 + 1); 

ou

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

Je me rends compte que c'est une façon vraiment simpliste de regarder mais c'est illustratif, n'est-ce pas? Utiliser une référence si elle va être utilisée plusieurs fois - par exemple, qui de ces exemples est plus logique:

$(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); 

Le second est plus pratique, même si elle a obtenu plus de lignes J'espère que tout cela est utile. (et la syntaxe de jquery n'a renvoyé personne)

0

OUI!Les fonctions anonymes sont plus rapides que les fonctions régulières. Peut-être que si la rapidité est de la plus haute importance ... plus importante que la réutilisation du code, pensez à utiliser des fonctions anonymes.

Il y a un très bon article sur l'optimisation javascript et les fonctions anonymes ici:

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

0

@nickf

(voudrais avoir le représentant à un commentaire, mais je ne l'ai juste trouvé Mon point est qu'il y a confusion ici entre les fonctions nommées/anonymes et le cas d'utilisation de l'exécution de + compilation dans une 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 qui est défectueux. Cela me semble évident, mais sinon je pense que le meilleur conseil est de "ne pas faire des choses stupides" (dont le changement de bloc constant + création d'objet de ce cas d'utilisation est un) et si vous n'êtes pas Bien sûr, testez!

1

Les objets anonymes sont plus rapides que les objets nommés. Mais appeler plus de fonctions coûte plus cher, et à un degré qui éclipse toutes les économies que vous pourriez obtenir en utilisant des fonctions anonymes. Chaque fonction appelée s'ajoute à la pile d'appels, ce qui introduit une quantité de surcharge légère mais non triviale. Mais à moins que vous n'écriviez des routines de chiffrement/décryptage ou quelque chose de similaire à la performance, comme beaucoup d'autres l'ont noté, il est toujours préférable d'optimiser le code rapide et facile à lire.

En supposant que vous écrivez du code bien architecturé, alors les problèmes de vitesse devraient être la responsabilité de ceux qui écrivent les interpréteurs/compilateurs.

1

Nous pouvons avoir un impact sur les performances dans le fonctionnement des fonctions de déclaration. Voici une référence de déclarer des fonctions dans le cadre 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 en dehors, mais dans Firefox, il est le contraire.

Dans d'autres exemple, nous voyons que si la fonction intérieure n'est pas une pure fonction, il aura un manque de performance aussi dans Firefox: http://jsperf.com/function-context-benchmark-3

0

Comme indiqué dans les commentaires à @nickf Réponse: La réponse à

crée une fonction fois plus vite que la création d'un million de fois

est tout simplement oui. Mais comme le montre sa perf de JS, il n'est pas plus lent d'un facteur de un million, ce qui montre qu'il devient plus rapide au fil du temps.

La question la plus intéressante pour moi est:

Comment un répété créer + exécuter comparer pour créer une fois + répétée run.

Si une fonction effectue un calcul complexe, le temps de création de l'objet fonction est probablement négligeable. Mais qu'en est-il de la tête de créer dans les cas où exécuter 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 juste une fois est plus rapide que prévu. Cependant, même avec une opération très rapide comme un simple ajout, le surcoût de la création répétée de la fonction n'est que de quelques pourcents.

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

Questions connexes