J'ai récemment commencé à maintenir le code JavaScript de quelqu'un d'autre. Je corrige des bogues, j'ajoute des fonctionnalités et j'essaie également de mettre de l'ordre dans le code et de le rendre plus cohérent.
Le développeur précédent utilise deux façons de déclarer les fonctions et je n’arrive pas à savoir s’il y a une raison derrière tout cela.
Ces deux méthodes sont les suivantes :
var functionOne = function() {
// Some code
};
function functionTwo() {
// Some code
}
Quelles sont les raisons d'utiliser ces deux méthodes différentes et quels sont les avantages et les inconvénients de chacune ? Y a-t-il quelque chose que l'on peut faire avec une méthode qui ne peut pas être fait avec l'autre ?
La différence est que functionOne
est une expression de fonction et n'est donc définie que lorsque cette ligne est atteinte, alors que functionTwo
est une déclaration de fonction et est définie dès que la fonction ou le script qui l'entoure est exécuté (en raison du [hoisting][1]).
Par exemple, une expression de fonction :
// TypeError: functionOne is not a function
functionOne();
var functionOne = function() {
console.log("Hello!");
};
Et, une déclaration de fonction :
// Outputs: "Hello!"
functionTwo();
function functionTwo() {
console.log("Hello!");
}
Cela signifie également que vous ne pouvez pas définir des fonctions de manière conditionnelle en utilisant des déclarations de fonctions :
if (test) {
// Error or misbehavior
function functionThree() { doSomething(); }
}
L'exemple ci-dessus définit en fait functionThree
indépendamment de la valeur de test
; à moins que use strict
ne soit en vigueur, auquel cas il soulève simplement une erreur.
[1] : http://adripofjavascript.com/blog/drips/variable-and-function-hoisting.html
Tout d'abord, je veux corriger Greg : function abc(){}
est trop scopé &mdash ; le nom abc
est défini dans la portée où cette définition est rencontrée. Exemple :
function xyz(){
function abc(){};
// abc is defined here...
}
// ...but not here
Deuxièmement, il est possible de combiner les deux styles :
var xyz = function abc(){};
xyz
va être défini comme d'habitude, abc
est indéfini dans tous les navigateurs sauf Internet Explorer &mdash ; ne comptez pas sur sa définition. Mais il sera défini à l'intérieur de son corps :
var xyz = function abc(){
// xyz is visible here
// abc is visible here
}
// xyz is visible here
// abc is undefined here
Si vous voulez aliaser des fonctions sur tous les navigateurs, utilisez ce type de déclaration :
function abc(){};
var xyz = abc;
Dans ce cas, xyz
et abc
sont tous deux des alias du même objet :
console.log(xyz === abc); // prints "true"
Une raison impérieuse d'utiliser le style combiné est l'attribut "name" des objets fonction (non supporté par Internet Explorer). En fait, lorsque vous définissez une fonction comme
function abc(){};
console.log(abc.name); // prints "abc"
son nom est automatiquement attribué. Mais lorsque vous la définissez comme
var abc = function(){};
console.log(abc.name); // prints ""
son nom est vide &mdash ; nous avons créé une fonction anonyme et l'avons assignée à une variable.
Une autre bonne raison d'utiliser le style combiné est d'utiliser un nom interne court pour se référer à lui-même, tout en fournissant un nom long non contradictoire pour les utilisateurs externes :
// Assume really.long.external.scoped is {}
really.long.external.scoped.name = function shortcut(n){
// Let it call itself recursively:
shortcut(n - 1);
// ...
// Let it pass itself as a callback:
someFunction(shortcut);
// ...
}
Dans l'exemple ci-dessus, nous pourrions faire la même chose avec un nom externe, mais ce serait trop lourd (et plus lent).
(Une autre façon de se référer à soi-même est d'utiliser arguments.callee
, ce qui est encore relativement long, et n'est pas supporté dans le mode strict.)
Au fond, JavaScript traite les deux déclarations différemment. Ceci est une déclaration de fonction :
function abc(){}
abc
ici est défini partout dans la portée actuelle :
// We can call it here
abc(); // Works
// Yet, it is defined down there.
function abc(){}
// We can call it again
abc(); // Works
De plus, il est hissé à travers une déclaration return
:
// We can call it here
abc(); // Works
return;
function abc(){}
Ceci est une expression de fonction :
var xyz = function(){};
xyz
ici est défini à partir du point d'affectation :
// We can't call it here
xyz(); // UNDEFINED!!!
// Now it is defined
xyz = function(){}
// We can call it here
xyz(); // works
La déclaration de fonction par rapport à l'expression de fonction est la vraie raison pour laquelle il y a une différence démontrée par Greg.
Fait amusant :
var xyz = function abc(){};
console.log(xyz.name); // Prints "abc"
Personnellement, je préfère la déclaration "expression de fonction" car de cette façon, je peux contrôler la visibilité. Lorsque je définis la fonction comme
var abc = function(){};
je sais que j'ai défini la fonction localement. Lorsque je définis la fonction comme
abc = function(){};
je sais que je l'ai définie globalement à condition que je n'aie pas défini abc
n'importe où dans la chaîne des scopes. Ce style de définition est résistant même lorsqu'il est utilisé dans eval()
. Alors que la définition
function abc(){};
dépend du contexte et peut vous laisser deviner où il est réellement défini, surtout dans le cas de eval()
&mdash ; la réponse est : cela dépend du navigateur.
En termes d'informatique, on parle de fonctions anonymes et de fonctions nommées. Je pense que la différence la plus importante est qu'une fonction anonyme n'est pas liée à un nom, d'où le nom de fonction anonyme. En JavaScript, il s'agit d'un objet de première classe déclaré dynamiquement au moment de l'exécution.
Pour plus d'informations sur les fonctions anonymes et le lambda calculus, Wikipedia est un bon point de départ (http://en.wikipedia.org/wiki/Anonymous_function).