Sommaire
Pouvez-vous expliquer le raisonnement derrière la syntaxe des fonctions anonymes encapsulées en JavaScript? Pourquoi ça marche: (function(){})();
mais ça ne marche pas function(){}();
:?
Ce que je sais
En JavaScript, on crée une fonction nommée comme ceci:
function twoPlusTwo(){
alert(2 + 2);
}
twoPlusTwo();
Vous pouvez également créer une fonction anonyme et l'affecter à une variable:
var twoPlusTwo = function(){
alert(2 + 2);
};
twoPlusTwo();
Vous pouvez encapsuler un bloc de code en créant une fonction anonyme, puis en l'encapsulant entre crochets et en l'exécutant immédiatement:
(function(){
alert(2 + 2);
})();
Cela est utile lors de la création de scripts modularisés, pour éviter d'encombrer la portée actuelle ou la portée globale, avec des variables potentiellement conflictuelles - comme dans le cas des scripts Greasemonkey, des plugins jQuery, etc.
Maintenant, je comprends pourquoi cela fonctionne. Les crochets entourent le contenu et exposent uniquement le résultat (je suis sûr qu'il existe une meilleure façon de décrire cela), comme avec (2 + 2) === 4
.
Ce que je ne comprends pas
Mais je ne comprends pas pourquoi cela ne fonctionne pas aussi bien:
function(){
alert(2 + 2);
}();
Pouvez-vous me l'expliquer?
la source
Réponses:
Cela ne fonctionne pas car il est analysé comme un
FunctionDeclaration
, et l'identifiant de nom des déclarations de fonction est obligatoire .Lorsque vous l'entourez de parenthèses, il est évalué comme un
FunctionExpression
, et les expressions de fonction peuvent être nommées ou non.La grammaire d'un
FunctionDeclaration
ressemble à ceci:Et
FunctionExpression
s:Comme vous pouvez le voir, le jeton
Identifier
(Identifier opt )FunctionExpression
est facultatif, nous pouvons donc avoir une expression de fonction sans nom défini:Ou expression de fonction nommée :
Les parenthèses (officiellement appelé l'opérateur de regroupement ) ne peuvent entourer que les expressions et une expression de fonction est évaluée.
Les deux productions grammaticales peuvent être ambiguës, et elles peuvent être exactement identiques, par exemple:
L'analyseur sait si c'est un
FunctionDeclaration
ou unFunctionExpression
, selon le contexte où il apparaît.Dans l'exemple ci-dessus, le second est une expression car l' opérateur Virgule peut également gérer uniquement les expressions.
D'un autre côté, les
FunctionDeclaration
s ne peuvent en fait apparaître que dans ce qu'on appelle leProgram
code " ", ce qui signifie du code à l'extérieur dans la portée globale et à l'intérieurFunctionBody
des autres fonctions.Les fonctions à l'intérieur des blocs doivent être évitées, car elles peuvent entraîner un comportement imprévisible, par exemple:
Le code ci-dessus devrait en fait produire un
SyntaxError
, car aBlock
ne peut contenir que des instructions (et la spécification ECMAScript ne définit aucune instruction de fonction), mais la plupart des implémentations sont tolérantes et prendront simplement la deuxième fonction, celle qui alerte'false!'
.Les implémentations de Mozilla -Rhino, SpiderMonkey, - ont un comportement différent. Leur grammaire contient un énoncé de fonction non standard , ce qui signifie que la fonction sera évaluée au moment de l' exécution , et non au moment de l'analyse, comme cela se produit avec
FunctionDeclaration
s. Dans ces implémentations, nous obtiendrons la première fonction définie.Les fonctions peuvent être déclarées de différentes manières, comparez les éléments suivants :
1- Une fonction définie avec le constructeur Function affecté à la variable multiply :
2- Une déclaration de fonction d'une fonction nommée multiplier :
3- Une expression de fonction affectée à la variable multiplier :
4- Une expression de fonction nommée func_name , affectée à la variable multiplier :
la source
Même s'il s'agit d'une vieille question et réponse, il aborde un sujet qui, à ce jour, incite de nombreux développeurs à boucler. Je ne peux pas compter le nombre de candidats développeurs JavaScript que j'ai interviewés qui ne pouvaient pas me faire la différence entre une déclaration de fonction et une expression de fonction et qui n'avaient aucune idée de ce qu'est une expression de fonction immédiatement invoquée.
Je voudrais mentionner, cependant, une chose très importante qui est que l'extrait de code de Premasagar ne fonctionnerait pas même s'il lui avait donné un identifiant de nom.
La raison pour laquelle cela ne fonctionnerait pas est que le moteur JavaScript l'interprète comme une déclaration de fonction suivie d'un opérateur de regroupement complètement indépendant qui ne contient aucune expression, et les opérateurs de regroupement doivent contenir une expression. Selon JavaScript, l'extrait de code ci-dessus est équivalent au suivant.
Une autre chose que je voudrais souligner qui peut être utile à certaines personnes est que tout identifiant de nom que vous fournissez pour une expression de fonction est à peu près inutile dans le contexte du code, sauf dans la définition de la fonction elle-même.
Bien sûr, utiliser des identifiants de nom avec vos définitions de fonction est toujours utile quand il s'agit de déboguer du code, mais c'est autre chose entièrement ... :-)
la source
D'excellentes réponses ont déjà été publiées. Mais je tiens à noter que les déclarations de fonction renvoient un enregistrement d'achèvement vide:
Ce fait n'est pas facile à observer, car la plupart des façons de tenter d'obtenir la valeur renvoyée convertiront la déclaration de fonction en une expression de fonction. Cependant, le
eval
montre:Appeler un enregistrement d'achèvement vide n'a aucun sens. Voilà pourquoi ça
function f(){}()
ne marche pas. En fait, le moteur JS n'essaie même pas de l'appeler, les parenthèses sont considérées comme faisant partie d'une autre instruction.Mais si vous encapsulez la fonction entre parenthèses, elle devient une expression de fonction:
Les expressions de fonction renvoient un objet fonction. Et par conséquent , vous pouvez l' appeler:
(function f(){})()
.la source
En javascript, cela s'appelle l' expression de fonction immédiatement invoquée (IIFE) .
Pour en faire une expression de fonction, vous devez:
enfermez-le en utilisant ()
placer un opérateur vide devant lui
l'assigner à une variable.
Sinon, il sera traité comme une définition de fonction et vous ne pourrez pas l'appeler / l'invoquer en même temps de la manière suivante:
Ce qui précède vous donnera une erreur. Parce que vous ne pouvez appeler qu'une expression de fonction immédiatement.
Ceci peut être réalisé de deux manières: Voie 1:
Voie 2:
Voie 3:
voie 4:
Tout ce qui précède invoquera immédiatement l'expression de fonction.
la source
J'ai juste une autre petite remarque. Votre code fonctionnera avec un petit changement:
J'utilise la syntaxe ci-dessus au lieu de la version la plus répandue:
car je n'ai pas réussi à faire fonctionner correctement l'indentation pour les fichiers javascript dans vim. Il semble que vim n'aime pas les accolades à l'intérieur des parenthèses ouvertes.
la source
function
mot - clé comme une déclaration de fonction, auquel cas la fin()
est interprétée comme un opérateur de regroupement qui, selon les règles de syntaxe JavaScript, peut uniquement et doit contenir une expression JavaScript.()
est incluse dans l'opérateur de regroupement (celle que Douglas Crockford recommande fortement) pour la cohérence: c'est Il est courant d'utiliser les IIFE sans les affecter à une variable, et il est facile d'oublier d'inclure ces parenthèses d'habillage si vous ne les utilisez pas de manière cohérente.La réponse la plus courte serait peut-être que
est un littéral de fonction qui définit une fonction (anonyme). Une paire supplémentaire (), qui est interprétée comme une expression, n'est pas attendue au niveau supérieur, uniquement les littéraux.
se trouve dans une instruction d'expression qui appelle une fonction anonyme.
la source
La syntaxe ci-dessus est valide car tout ce qui est passé entre parenthèses est considéré comme une expression de fonction.
La syntaxe ci-dessus n'est pas valide. Parce que l'analyseur de syntaxe du script java recherche le nom de la fonction après le mot-clé de la fonction, car il ne trouve rien, il génère une erreur.
la source
Vous pouvez également l'utiliser comme:
ou
!
- negation op convertit la définition de fn en expression de fn, vous pouvez donc l'invoquer immédiatement avec()
. Identique à l'utilisation de0,fn def
ouvoid fn def
la source
Ils peuvent être utilisés avec des paramètres-arguments comme
résulterait en 7
la source
Ces parenthèses supplémentaires créent des fonctions anonymes supplémentaires entre l'espace de noms global et la fonction anonyme qui contient le code. Et en Javascript, les fonctions déclarées dans d'autres fonctions ne peuvent accéder qu'à l'espace de noms de la fonction parent qui les contient. Comme il existe un objet supplémentaire (fonction anonyme) entre la portée globale et la portée du code réel n'est pas conservé.
la source