Question

Je me demandais, existe-t-il une différence de performances entre l'utilisation de fonctions nommées et de fonctions anonymes en 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;
}

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

Était-ce utile?

La solution

Le problème de performances réside ici dans le coût de création d'un nouvel objet fonction à chaque itération de la boucle et non dans l'utilisation d'une fonction anonyme:

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

Vous créez un millier d'objets de fonction distincts même s'ils ont le même corps de code et aucune liaison avec la portée lexicale ( fermeture ). Ce qui suit semble en revanche plus rapide, car il attribue simplement la même référence de la 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 n'attribuez des références aux éléments du tableau que lorsque vous y êtes dans la boucle, vous constaterez qu'il n'y a aucune différence de performance ou sémantique par rapport à la version de la fonction nommée. :

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

En résumé, l’utilisation de fonctions anonymes par rapport à des fonctions nommées n’entraîne aucun coût en performances observable.

En passant, 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 le second est une affectation de variable à 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 vous recommande de lire « ambiguïté de la déclaration de la fonction JavaScript ».

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

Autres conseils

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 reconnaisse pas qu'il s'agisse de la même fonction dans Test2 et le compile à chaque fois.

En règle générale, évitez d'appliquer plusieurs fois le même code. Au lieu de cela, vous devriez extraire du code commun dans une fonction et exécuter cette fonction (générale, bien testée, facile à modifier) ??à partir de plusieurs emplacements.

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’avez rien d’autre dans votre programme), puis une fonction autonome probablement (c’est un devinez les gens) est 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.

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

Si vous trouvez le code plus facile à assimiler, les performances ne sont pas un problème, sauf si vous prévoyez d'appeler la fonction des millions de fois.

Les objets anonymes sont plus rapides que les objets nommés. Mais appeler plus de fonctions coûte plus cher, et à un point qui éclipse 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 surcharge légère mais non négligeable.

Mais à moins d'écrire des routines de cryptage / décryptage ou quelque chose d'aussi sensible aux performances, comme d'autres l'ont déjà noté, il est toujours préférable d'optimiser un code élégant, facile à lire, par rapport à un code rapide.

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

Nous pouvons avoir un impact sur les performances grâce au fonctionnement des fonctions de déclaration. Voici un repère de déclaration de fonctions dans le contexte d’une autre fonction ou à l’extérieur:

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

Sous Chrome, l'opération est plus rapide si nous déclarons la fonction à l'extérieur, mais sous Firefox, c'est l'inverse.

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

Ce qui va certainement rendre votre boucle plus rapide sur plusieurs navigateurs, en particulier les navigateurs IE, se passe comme suit:

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

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

une référence sera presque toujours plus lente que la chose à laquelle elle fait référence. Pensez-y de cette façon: supposons que vous vouliez imprimer le résultat de l’ajout de 1 + 1. Ce qui est plus logique:

alert(1 + 1);

ou

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

Je réalise que c'est une façon très simpliste de voir les choses, mais c'est illustratif, non? Utilisez une référence uniquement si elle doit ê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);

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

@ nickf

(j'aimerais avoir le représentant pour commenter, mais je viens juste de trouver ce site)

Mon point est qu'il y a une confusion ici entre les fonctions nommées / anonymes et le cas d'utilisation de l'exécution + de la 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 "ne faites pas de bêtises". (dont le décalage constant de bloc + 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 habituelles. Peut-être que si la vitesse est de la plus haute importance ... plus importante que la réutilisation de code, envisagez d'utiliser des fonctions anonymes.

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

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

@ nickf

C’est un test assez fastueux, vous comparez le temps d’exécution et de compilation qui va évidemment coûter 1 fois la méthode (compile N fois, le moteur JS en fonction) avec la méthode 2 ) Je ne peux pas imaginer un développeur JS qui transmettrait son code de probation de cette manière.

L’assignation anonyme est une approche beaucoup plus réaliste, car vous utilisez en fait votre méthode document.onclick, qui ressemble plutôt à la suivante, qui privilégie légèrement la méthode anon.

Utiliser un framework de test similaire au 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");
}

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

  

Crée une fonction une fois plus vite que la créer un million de fois

est simplement oui. Mais comme le montre sa performance JS, il ne ralentit pas d’un facteur 1 000, ce qui montre qu’il devient de plus en plus rapide au fil du temps.

La question la plus intéressante pour moi est:

  

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

Si une fonction effectue un calcul complexe, le temps nécessaire pour créer l'objet fonction est probablement négligeable. Mais qu’en est-il de la surcharge 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);
}

Ce JS Perf montre que la création de la fonction une seule fois est plus rapide car attendu. Cependant, même avec une opération très rapide comme un simple ajout, la création répétée de la fonction ne représente qu'une surcharge de quelques pour cent.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top