396 votes

Expliquer la syntaxe de la fonction anonyme encapsulée.

Resumen

Pouvez-vous expliquer le raisonnement qui sous-tend la syntaxe des fonctions anonymes encapsulées en JavaScript ? Pourquoi cela fonctionne-t-il ? (function(){})(); mais ce n'est pas le cas : 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'entourant de parenthèses et en l'exécutant immédiatement :

(function(){
    alert(2 + 2);
})();

Ceci est utile lors de la création de scripts modulaires, 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 ça marche. Les parenthèses enferment le contenu et n'exposent que le résultat (je suis sûr qu'il y a 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);
}();

Vous pouvez m'expliquer ça ?

44 votes

Je pense que toutes ces notations variées et ces façons de définir/établir/appeler les fonctions sont la partie la plus déroutante du travail initial avec javascript. Les gens ont tendance à ne pas en parler non plus. Ce n'est pas un point sur lequel on insiste dans les guides ou les blogs. Cela m'étonne parce que c'est quelque chose de déroutant pour la plupart des gens, et les personnes qui parlent couramment le js doivent être passées par là aussi. C'est comme une réalité taboue et vide dont on ne parle jamais.

1 votes

Lisez également ce qui concerne le objectif de cette construction ou vérifier un ( technique ) explication (également aquí ). Pour le placement de la parenthèse, voir cette question sur leur localisation .

0 votes

OT : Pour ceux qui veulent savoir où ces fonctions anonymes sont beaucoup utilisées, lisez svp adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html

430voto

CMS Points 315406

Cela ne fonctionne pas parce qu'il est analysé en tant que FunctionDeclaration et l'identificateur de nom des déclarations de fonctions est obligatoire .

Lorsque vous l'entourez de parenthèses, il est évalué comme un FunctionExpression et les expressions de fonctions peuvent être nommées ou non.

La grammaire d'un FunctionDeclaration ressemble à ça :

function Identifier ( FormalParameterListopt ) { FunctionBody }

Et FunctionExpression s :

function Identifieropt ( FormalParameterListopt ) { FunctionBody }

Comme vous pouvez le constater, le Identifier (Identifiant opt ) dans FunctionExpression est facultatif, on peut donc avoir une expression de fonction sans qu'un nom soit défini :

(function () {
    alert(2 + 2);
}());

Ou nommé l'expression de la fonction :

(function foo() {
    alert(2 + 2);
}());

Les Parenthèses (formellement appelées l'opérateur de regroupement ) ne peuvent entourer que des expressions, et une expression de fonction est évaluée.

Les deux productions grammaticales peuvent être ambiguës, et elles peuvent avoir exactement la même apparence, par exemple :

function foo () {} // FunctionDeclaration

0,function foo () {} // FunctionExpression

Le parseur sait si c'est un FunctionDeclaration ou un FunctionExpression en fonction de la contexte où il apparaît.

Dans l'exemple ci-dessus, le second est une expression car le fichier Opérateur de virgule peut également ne traiter que des expressions.

D'un autre côté, FunctionDeclaration pourraient en fait n'apparaître que dans ce que l'on appelle les " Program ", c'est-à-dire le code situé à l'extérieur dans la portée globale, et à l'intérieur de la portée de l'utilisateur. FunctionBody d'autres fonctions.

Les fonctions à l'intérieur des blocs doivent être évitées, car elles peuvent entraîner un comportement imprévisible, par exemple :

if (true) {
  function foo() {
    alert('true');
  }
} else {
  function foo() {
    alert('false!');
  }
}

foo(); // true? false? why?

Le code ci-dessus devrait en fait produire un SyntaxError puisque a Block ne peut contenir que des déclarations (et la spécification ECMAScript ne définit aucune déclaration 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 Mozilla -Rhino, SpiderMonkey,- ont un comportement différent. Leur grammaire contient un hors normes Déclaration de fonction, ce qui signifie que la fonction sera évaluée à temps d'exécution et non pas au moment de l'analyse syntaxique, comme c'est le cas avec le système 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 Fonction assigné à la variable multiplier :

var multiply = new Function("x", "y", "return x * y;");

2- Une déclaration de fonction d'une fonction nommée multiplier :

function multiply(x, y) {
    return x * y;
}

3- Une expression de fonction affectée à la variable multiplier :

var multiply = function (x, y) {
    return x * y;
};

4- Une expression de fonction nommée nom_func affecté à la variable multiplier :

var multiply = function func_name(x, y) {
    return x * y;
};

3 votes

La réponse de la CMS est correcte. Pour une excellente explication approfondie des déclarations de fonctions et des expressions, voir cet article de kangax .

0 votes

C'est une excellente réponse. Dans votre exemple 3, devrais-je dire qu'il s'agit d'une expression de fonction parce qu'elle suit un signe égal, alors que cette forme est une déclaration de fonction lorsqu'elle apparaît sur une ligne seule ? Je me demande à quoi cela servirait-il ? Est-il simplement interprété comme une déclaration de fonction nommée, mais sans nom ? À quoi cela sert-il si vous ne l'assignez pas à une variable, ne la nommez pas et ne l'appelez pas ?

1 votes

Aha. Très utile. Merci, CMS. Cette partie de la documentation de Mozilla dont vous avez fait le lien est particulièrement instructive : developer.mozilla.org/Fr/Core_JavaScript_1.5_Reference/

56voto

natlee75 Points 1189

Même s'il s'agit d'une ancienne question-réponse, elle aborde un sujet qui, à ce jour, laisse de nombreux développeurs perplexes. Je ne compte plus le nombre de candidats développeurs JavaScript que j'ai interrogés et qui étaient incapables de faire la différence entre une déclaration de fonction et une expression de fonction. y qui n'avait aucune idée de ce qu'est une expression de fonction immédiatement invoquée.

J'aimerais cependant mentionner une chose très importante, à savoir que le bout de code de Premasagar ne fonctionnerait pas même s'il lui avait donné un identifiant de nom.

function someName() {
    alert(2 + 2);
}();

La raison pour laquelle cela ne fonctionnerait pas est que le moteur JavaScript interprète cela comme une déclaration de fonction suivie d'un opérateur de regroupement sans aucun rapport avec la fonction et ne contenant aucune expression, et les opérateurs de regroupement debe contient une expression. Selon JavaScript, le bout de code ci-dessus est équivalent au suivant.

function someName() {
    alert(2 + 2);
}

();

Une autre chose que je voudrais souligner et qui pourrait être utile à certaines personnes est que tout identifiant de nom que vous fournissez pour une expression de fonction est pratiquement inutile dans le contexte du code, sauf dans la définition de la fonction elle-même.

var a = function b() {
    // do something
};
a(); // works
b(); // doesn't work

var c = function d() {
    window.setTimeout(d, 1000); // works
};

Bien sûr, l'utilisation d'identificateurs de noms avec vos définitions de fonctions est toujours utile lorsqu'il s'agit de déboguer du code, mais c'est une toute autre histoire... :-)

21voto

Oriol Points 20803

D'excellentes réponses ont déjà été postées. Mais je tiens à souligner que les déclarations de fonction renvoient un enregistrement d'achèvement vide :

14.1.20 - Sémantique de l'exécution : Évaluation

Déclaration de fonction : function BindingIdentifier ( FormalParameters ) { FonctionCorps }

  1. Retourner à NormalCompletion (vide).

Ce fait n'est pas facile à observer, car la plupart des façons de tenter d'obtenir la valeur retournée convertissent la déclaration de fonction en une expression de fonction. Cependant, eval le montre :

var r = eval("function f(){}");
console.log(r); // undefined

Appeler un enregistrement d'achèvement vide n'a aucun sens. C'est pourquoi function f(){}() ne peut pas fonctionner. En fait, le moteur JS ne tente même pas de l'appeler, les parenthèses sont considérées comme faisant partie d'une autre déclaration.

Mais si vous mettez la fonction entre parenthèses, elle devient une expression de fonction :

var r = eval("(function f(){})");
console.log(r); // function f(){}

Les expressions de fonction renvoient un objet fonction. Et donc vous pouvez l'appeler : (function f(){})() .

15voto

asmmahmud Points 1949

En javascript, cela s'appelle Expression fonctionnelle provoquée immédiatement (IIFE) .

Pour en faire une expression fonctionnelle, il faut.. :

  1. l'enfermer en utilisant ()

  2. placer un opérateur void avant lui

  3. l'affecter à une variable.

Sinon, il sera traité comme une définition de fonction et il ne sera pas possible de l'appeler ou de l'invoquer en même temps de la manière suivante :

 function (arg1) { console.log(arg1) }(); 

Ce qui précède vous donnera une erreur. Parce que vous ne pouvez invoquer une expression de fonction qu'immédiatement.

Cela peut se faire de plusieurs façons : Méthode 1 :

(function(arg1, arg2){
//some code
})(var1, var2);

Voie 2 :

(function(arg1, arg2){
//some code
}(var1, var2));

Voie 3 :

void function(arg1, arg2){
//some code
}(var1, var2);

façon 4 :

  var ll = function (arg1, arg2) {
      console.log(arg1, arg2);
  }(var1, var2);

Tout ce qui précède invoquera immédiatement l'expression de la fonction.

11voto

Tim Down Points 124501

La réponse de la CMS est correcte. Pour une excellente explication approfondie des déclarations de fonctions et des expressions, voir cet article de kangax .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X