web-dev-qa-db-fra.com

JavaScript: fonctions en ligne vs fonctions prédéfinies

Est-ce que n'importe quel corps peut me lancer des arguments pour utiliser fonctions en ligne contre le passage fonction prédéfinie nom à un gestionnaire.

C'est à dire. ce qui est mieux:

(function() {
  setTimeout(function() { /*some code here*/ }, 5);
})();

versus

(function() {
  function invokeMe() {
    /*code*/
  }
  setTimeout(invokeMe, 5);
})();


Étrange question, mais nous nous battons presque dans l'équipe à ce sujet.

44
glaz666

Fonctions nommées

Il y a une grave mauvaise utilisation de la terminologie dans les questions et réponses sur cette page. Il n'y a rien sur le fait qu'une fonction soit ou non en ligne (une expression de fonction) qui dit que vous ne pouvez pas la nommer.

Ceci utilise une expression de fonction :

setTimeout(function doSomethingLater() { alert('In a named function.'); }, 5);

et ceci utilise une instruction de fonction :

function doSomethingLater() { alert('In a named function.'); }
setTimeout(doSomethingLater, 5);

Les deux exemples utilisent des fonctions nommées et bénéficient tous les deux des mêmes avantages en termes de débogage et d'outils de profilage !

Si le nom est spécifié (le texte après "fonction" mais avant la parenthèse), il s'agit d'une fonction nommée, qu'elle soit en ligne ou déclarée séparément. Si le nom n'est pas spécifié, il est "anonyme".

Remarque: T.J. souligne que IE traite mal les expressions de fonction nommées de manière non triviale (Voir: http://kangax.github.com/nfe/#jscript-bugs ) et c'est important à noter, j'essaie simplement de faire un point sur la terminologie.

Lequel devriez-vous utiliser?

En réponse à votre question directe, vous devez utiliser une instruction de fonction nommée si la fonction peut être utilisée à partir de n'importe quel autre endroit de votre code. Si la fonction est utilisée à exactement un endroit et n'a aucune pertinence ailleurs, j'utiliserais une expression de fonction à moins qu'elle soit trop longue ou qu'elle ne soit pas à sa place (pour des raisons de style). Si vous utilisez une expression de fonction en ligne, il est souvent utile de la nommer de toute façon à des fins de débogage ou de clarté du code.

Fuites de mémoire

Que vous nommiez votre fonction, utilisiez une instruction de fonction ou utilisiez une expression de fonction a peu d'impact sur le problème de fuite de mémoire. Permettez-moi d'essayer d'expliquer les causes de ces fuites. Jetez un oeil à ce code:

(function outerFunction() {
    var A = 'some variable';

   doStuff();
})();

Dans le code ci-dessus, lorsque "externalFunction" se termine, "A" sort de la portée et peut être récupéré, libérant cette mémoire.

Et si on y ajoute une fonction?

(function outerFunction() {
    var A = 'some variable';

   setTimeout(function(){ alert('I have access to A whether I use it or not'); }, 5);
})();

Dans ce code (ci-dessus), l'expression de fonction que nous transmettons à setTimeout a une référence à "A" (par la magie de la fermeture) et même après la fin de "externalFunction" "A" restera dans mémoire jusqu'à ce que le délai soit déclenché et que la fonction soit déréférencée .

Et si nous passons cette fonction à autre chose que setTimeout?

(function outerFunction() {
    var A = 'some variable';

   doStuff(function(){ alert('I have access to A whether I use it or not'); });
})();

function doStuff(fn) {
    someElement.onclick = fn;
}

Maintenant, l'expression de fonction que nous passons à "doStuff" a accès à "A" et même après la fin de "externalFunction" "A" restera en mémoire tant qu'il y aura une référence à la fonction que nous avons passée dans doStuff . Dans ce cas, nous créons une référence à cette fonction (en tant que gestionnaire d'événements) et donc "A" restera en mémoire jusqu'à ce que ce gestionnaire d'événements soit effacé. (par exemple, quelqu'un appelle someElement.onclick = null)

Regardez maintenant ce qui se passe lorsque nous utilisons une instruction de fonction:

(function outerFunction() {
    var A = 'some variable';

    function myFunction() { alert('I have also have access to A'); };
    doStuff(myFunction);
})();

Le même problème! "myFunction" ne sera nettoyé que si "doStuff" ne contient pas de référence et "A" ne sera nettoyé que lorsque "myFunction" sera nettoyé. Peu importe que nous ayons utilisé une déclaration ou une expression; ce qui compte, c'est si une référence à cette fonction est créée dans "doStuff"!

52
Prestaul

Il y a un différence significative entre les deux: ce dernier a un nom.

J'aime aider mes outils à m'aider, et donc j'aime surtout éviter les fonctions anonymes car mes outils ne peuvent pas me donner des informations significatives à leur sujet (par exemple, dans une liste de pile d'appels dans un débogueur, etc. ). J'irais donc avec le

(function(){
  function invokeMe() {
    /*code*/
  }
  setTimeout(invokeMe, 5);
})();

... forme en général. Les règles sont censées être enfreintes, mais pas servies servilement. :-)

Notez que selon la spécification, il existe une troisième alternative: vous pouvez avoir une fonction en ligne qui a également un nom:

(function(){
  setTimeout(function invokeMe(){ /*some code here*/ }, 5);
})();

Le problème, cependant, est que jusqu'à présent, chaque version de l'interpréteur JavaScript de Microsoft ("JScript"), y compris (étonnamment) celle d'IE9, gère incorrectement expression de fonction nommée et crée deux fonctions complètement distinctes à des moments différents. ( Proof , essayez-le dans IE9 ou une version antérieure et également dans à peu près n'importe quel autre navigateur.) IE se trompe de deux manières: 1. Il crée deux fonctions distinctes objets, et 2. En conséquence de l'un d'eux, il "saigne" le symbole du nom dans la portée englobante de l'expression (en violation claire de Section 1 de la spécification). Détails ici: - Double prise

11
T.J. Crowder

IMO, déclarer une fonction ne sera utile que si vous avez l'intention de la réutiliser plus tard, d'une autre manière.

J'utilise personnellement les expressions de fonction (première manière) pour les gestionnaires setTimeout.

Cependant, vous voudrez peut-être connaître les différences entre les déclarations de fonction et les expressions de fonction, je vous recommande l'article suivant:

5
CMS

Je suggère un duel complet entre les membres de l'équipe adverse pour régler de tels arguments.

Plus sérieusement, au final, cela n'a pas d'importance. La première forme (fonctions non nommées) a tendance à devenir difficile à manier avec des fonctions plus grandes, mais n'est pas du tout un problème avec de petites fonctions (1-2 lignes). La deuxième forme est également inoffensive.

Tout argument contre l'un ou l'autre style est pur bikeshedding , imo.

3
jsight

Une fonction en ligne évite la pollution de l'espace de noms et les fonctions prédéfinies ont une réutilisation plus élevée. Je pense que vous pourriez faire des cas où chacun est approprié.

2
Mark

Ne pouvons-nous pas tous nous entendre?

(function(){
  setTimeout( (function InvokeMe(){ /*some code here*/ }), 5);
})();

Une seule chose compte vraiment, l'OMI et c'est la facilité de débogage. Beaucoup de traceurs d'étapes ne pourront rien vous dire sur le func, à part le fait qu'il était anonyme et qu'il y avait des arguments, mais vous pouvez toujours définir inline avec un nom en mettant la définition entre parenthèses pour forcer l'évaluation. Pour les fonctions de rupture très simples ou évidentes, je suppose que ce n'est pas un gros problème mais pour moi, c'est comme des demi-finales. Je ne me soucie vraiment pas de ce que fait l'autre gars si cela ne cause pas de douleur mais j'essaie de toujours nommer mes funcs car ce n'est pas difficile et cela peut être un avantage.

1
Erik Reppen

Les fonctions nommées prédéfinies peuvent réduire le problème d'enfer de rappel JavaScript, qui est mentionné à http://callbackhell.com/

1
Alireza Fattahi

Je sais que c'est une vieille question, mais pour moi il y a une différence encore plus importante que celles déjà mentionnées. levage Chaque fonction doit être créée et donc réserve un peu d'espace en mémoire et doit être éventuellement GC plus tard.

Les fonctions nommées sont hissées au début de la fonction environnante et sont donc créées à chaque appel de fonction, qu'elles soient utilisées ou non. Les fonctions anonymes ne sont créées que si le code qui les définit est exécuté.

//an example where you wold prefer to use an anonymous function.
//you can assign this (anonymous) function to a variable, so you get your "name" back.
function someFn(){
    if(condition){
        //the variable declaration has been hoisted, 
        //but the function is created at this point, and only if necessary.
        var process = function(value){/* */};
        switch(condition2){
            case 1: process(valueFor1); break;
            case 2: process(valueFor2); break;
            /* ... */
        }
    }
}

function someFn(){
    var process;
    if(condition){
        process = function(value){ /* A */ }
    }else{
        process = function(value){ /* B */ }
    }

    //beware, depending on your code, "process" may be undefined or not a function
    process(someValue);
}


//an example where you would prefer (/ utilize) the hoisting.
function someFn(){
    /* some code */
    while(condition){
        //some might want to keep the function definition near the code where it is used,
        //but unlike an anonymous function or a lambda-expression this process-function 
        //is created only once per function-call, not once per iteration.
        function process(value, index){ /* ... */ }
        /* ... */
        process(value, index)
    }
}

donc, en règle générale:

  • à l'intérieur d'une boucle, il ne devrait pas y avoir de fonction anonyme ou d'expression lambda

  • si vous avez besoin de la fonction uniquement à l'intérieur d'une condition (rarement vraie), vous devriez préférer les fonctions anonymes aux fonctions nommées, car elles ne sont créées qu'en cas de besoin

  • si vous connaissez votre entreprise (JavaScript), vous savez quand ignorer ce conseil

1
Thomas

Je pense que la seule différence dans un code comme celui-ci est qu'avec le deuxième morceau de code vous pouvez ré-appeler la même fonction (parfois avec des "fonctions timer" c'est utile):

(function(){
  function invokeMe() {
    if(..) setTimeout(invokeMe, 5);
  }
  setTimeout(invokeMe, 5);
})();
1
mck89

J'ai également tendance à privilégier les fonctions nommées. Les références de fonctions anonymes sont rapides, mais ne doivent être utilisées que pour des choses simples. Ma règle de base est que si la fonction comporte plus de 2 lignes de code, elle appartient probablement à sa propre définition.

Ceci est compliqué par la plupart des exemples de code utilisant des fonctions anonymes. Mais les échantillons sont généralement très simplistes. La méthode s'effondre à mesure que les choses se compliquent. J'ai vu des références de fonction imbriquées dans des références de fonction lorsque le développeur s'est rendu compte que davantage de rappels étaient nécessaires dans les étapes suivantes. Au lieu de cette logique arborescente, je préfère l'organisation de fonctions isolées.

Et je finis généralement heureux de pouvoir réutiliser l'une des fonctions que je définis plus tard.

Une utilisation importante d'une fonction anonyme est lorsque vous avez besoin de transmettre des données de portée à votre appel de fonction, mais j'encapsule généralement ma fonction dans la fonction anonyme.

Les fonctions nommées sont également absolument nécessaires si vous vous lancez dans le développement piloté par les tests.

0
Ray Wadkins

Dans l'exemple fourni, la déclaration et l'utilisation de la fonction sont si proches que je pense que la seule différence est la lisibilité. Je préfère le deuxième exemple.

0
lincolnk

Il n'y a aucune raison technique de préférer une version à l'autre. Pour moi, cela dépend généralement de deux choses:

  1. Je souhaite reprendre le rappel passé dans un autre contexte. Dans ce cas, je définis la fonction autonome et passe la référence.
  2. Le rappel est supérieur à ~ 10 lignes de code et la fonction attend des arguments supplémentaires après le rappel. Dans ce cas, il est difficile de reconstruire, quelles valeurs sont réellement transmises à la fonction.

Exemple:

setTimeout(function() { // I need to scroll to see the other arguments

  // many lines of code

}, 0); // <- where does this '0' belong to?
0
Fabian Jakobs

Je préfère utiliser des fonctions nommées. Les fonctions nommées s'affichent par nom sur tous les débogueurs (air, firebug, IE).

Exemple:

Notez que vous pouvez également avoir des fonctions nommées en ligne comme

{
    method: function obj_method(){}
}

De cette façon, lorsque vous regardez la trace de la pile, vous verrez la fonction obj_method au lieu d'anonyme.

Demandez-vous quand incorporer une fonction plutôt que de la déclarer? Quand cela a du sens dans le code. Si vous en avez besoin de deux endroits différents, il ne peut pas être en ligne. Parfois en ligne, le code est plus facile à lire, parfois plus difficile.

0
Juan Mendes