Pourquoi avez-vous besoin d'invoquer une fonction anonyme sur la même ligne?

374

Je lisais des articles sur les fermetures et je l'ai vu partout, mais il n'y a pas d'explication claire sur le fonctionnement - à chaque fois, on m'a simplement dit de l'utiliser ...:

// Create a new anonymous function, to use as a wrapper
(function(){
    // The variable that would, normally, be global
    var msg = "Thanks for visiting!";

    // Binding a new function to a global object
    window.onunload = function(){
        // Which uses the 'hidden' variable
        alert( msg );
    };
// Close off the anonymous function and execute it
})();

Ok, je vois que nous allons créer une nouvelle fonction anonyme puis l'exécuter. Donc, après cela, ce code simple devrait fonctionner (et il fonctionne):

(function (msg){alert(msg)})('SO');

Ma question est quel genre de magie se produit ici? J'ai pensé que quand j'ai écrit:

(function (msg){alert(msg)})

alors une nouvelle fonction sans nom serait créée comme la fonction "" (msg) ...

mais alors pourquoi ça ne marche pas?

(function (msg){alert(msg)});
('SO');

Pourquoi doit-il être dans la même ligne?

Pourriez-vous s'il vous plaît me pointer quelques messages ou me donner une explication?

palig
la source
2
Dans d'autres langues, ils sont appelés Pointeurs de fonction ou Délégués, si vous souhaitez examiner les structures de niveau inférieur impliquées.
Chris Moschini
17
Tu as un ; en première ligne
Oliver Ni
Maintenant que vous savez comment cela fonctionne ... Ne l'utilisez pas. Nous devons arrêter d'écrire des fonctions anonymes . Avec juste quelques caractères supplémentaires, nous pouvons donner à nos fonctions un vrai nom et rendre le débogage du code Javascript tellement plus facile!
Stijn de Witt
1
La ligne (function (msg){alert(msg)})('SO');fonctionne entièrement seule. Cela n'a rien à voir avec l'autre fonction anonyme que vous avez publiée avant. Ce sont deux fonctions anonymes complètement distinctes. Vous devez appeler une fonction anonyme immédiatement car elle n'a pas de nom et ne peut plus être référencée par la suite.
Octopus

Réponses:

380

Déposez le point-virgule après la définition de la fonction.

(function (msg){alert(msg)})
('SO');

Ci-dessus devrait fonctionner.

Page DEMO: https://jsfiddle.net/e7ooeq6m/

J'ai discuté de ce type de modèle dans ce post:

jQuery et $ questions

ÉDITER:

Si vous regardez la spécification du script ECMA , il existe 3 façons de définir une fonction. (Page 98, section 13 Définition de la fonction)

1. Utilisation du constructeur de fonction

var sum = new Function('a','b', 'return a + b;');
alert(sum(10, 20)); //alerts 30

2. Utilisation de la déclaration de fonction.

function sum(a, b)
{
    return a + b;
}

alert(sum(10, 10)); //Alerts 20;

3. Expression de fonction

var sum = function(a, b) { return a + b; }

alert(sum(5, 5)); // alerts 10

Vous pouvez donc vous demander quelle est la différence entre la déclaration et l'expression?

De la spécification ECMA Script:

FunctionDeclaration: identificateur de fonction (FormalParameterListopt) {FunctionBody}

FunctionExpression: fonction Identifieropt (FormalParameterListopt) {FunctionBody}

Si vous remarquez, «identifiant» est facultatif pour l'expression de fonction. Et lorsque vous ne donnez pas d'identifiant, vous créez une fonction anonyme. Cela ne signifie pas que vous ne pouvez pas spécifier un identifiant.

Cela signifie que le suivi est valide.

var sum = function mySum(a, b) { return a + b; }

Un point important à noter est que vous ne pouvez utiliser «mySum» qu'à l'intérieur du corps de la fonction mySum, pas à l'extérieur. Voir l'exemple suivant:

var test1 = function test2() { alert(typeof test2); }

alert(typeof(test2)); //alerts 'undefined', surprise! 

test1(); //alerts 'function' because test2 is a function.

Démo en direct

Comparez cela à

 function test1() { alert(typeof test1) };

 alert(typeof test1); //alerts 'function'

 test1(); //alerts 'function'

Armés de ces connaissances, essayons d'analyser votre code.

Lorsque vous avez du code comme,

    function(msg) { alert(msg); }

Vous avez créé une expression de fonction. Et vous pouvez exécuter cette expression de fonction en la mettant entre parenthèses.

    (function(msg) { alert(msg); })('SO'); //alerts SO.
Yogi
la source
1
Ouais, mais pourquoi? Pourquoi doit-il être en ligne? Peu importe combien d'espace blanc j'utiliserai.
palig
9
Comme je l'ai écrit, le point-virgule a mis fin à la définition de fonction anonyme. Parce qu'il n'a pas de nom (c'est duh anonyme!), Vous ne pourrez plus l'appeler. Si vous ne mettez pas de point-virgule, la fonction pourrait toujours être exécutée.
SolutionYogi
Je pensais que l'insertion automatique de points-virgules mettrait un point-virgule dans ce cas, mais ce n'est pas le cas. Vous avez donc raison.
Nosredna
1
Nosredna, JS se comporte peu arbitrairement quand il s'agit d'ajouter des points-virgules. Lisez cet article détaillé: blog.boyet.com/blog/javascriptlessons/…
SolutionYogi
Oui, je vois que (fonction (msg) {alerte (msg)}) ('SO'); travaux. Je demandais juste pourquoi ça marche. Où cela est spécifié ou de quel type de fonctionnalité JS il s'agit. Donc, une fois que je viens d'appeler: (fonction (msg) {alerte (msg)}) qu'arrivera-t-il avec la fonction? Ce sera GC'ed?
palig
129

Cela s'appelle une fonction auto-invoquée.

Ce que vous faites lorsque vous appelez (function(){})renvoie un objet fonction. Lorsque vous y ajoutez (), il est invoqué et tout ce qui se trouve dans le corps est exécuté. Le ;dénote la fin de l'instruction, c'est pourquoi la 2e invocation échoue.

seth
la source
Ah ok je vois, donc c'est juste une syntaxe spéciale de JS, non? J'aime le plus cette explication! Simple et court :)
palig
Je pense qu'il est incorrect de dire que le corps sera «évalué». Il s'exécute comme n'importe quelle autre fonction. Parce qu'elle est anonyme, vous devez soit enregistrer la référence quelque part OU l'exécuter immédiatement.
SolutionYogi
16
Personnellement, je n'aime même pas le terme «fonction auto-invoquante». Ce n'est pas que la fonction s'invoque. Le programmeur a écrit ces parenthèses pour l'invoquer.
SolutionYogi
Ce n'est pas une "syntaxe spéciale" plus qu'autre chose n'est spécial. En fait, la forme "nom de la fonction (arguments) {BLOC}" est beaucoup plus "spéciale". Il s'agit en fait de sucre inutile; c'est pourtant ce qui fait réellement bouger les choses.
jrockway
2
joli lien vers l'article. Il note pourquoi quelqu'un utiliserait cette citation: "Dans un effort pour protéger l'objet global, toutes les applications JavaScript doivent être écrites dans une fonction auto-invoquante. Cela créera un champ d'application dans lequel des variables peuvent être créées sans craindre qu'elles n'entrent en collision avec d'autres applications. " Et a également noté "Une fois la fonction terminée, les variables sont ignorées et l'objet global reste inchangé."
yeahdixon
94

Une chose que j'ai trouvée confuse est que les "()" regroupent les opérateurs.

Voici votre fonction déclarée de base.

Ex. 1:

var message = 'SO';

function foo(msg) {
    alert(msg);
}

foo(message);

Les fonctions sont des objets et peuvent être regroupées. Jetons donc des parenthèses autour de la fonction.

Ex. 2:

var message = 'SO';

function foo(msg) {  //declares foo
    alert(msg);
}

(foo)(message);     // calls foo

Maintenant, au lieu de déclarer et d'appeler immédiatement la même fonction, nous pouvons utiliser la substitution de base pour la déclarer comme nous l'appelons.

Ex. 3.

var message = 'SO';

(function foo(msg) {
    alert(msg);
})(message);          // declares & calls foo

Enfin, nous n'avons pas besoin de ce truc supplémentaire parce que nous n'utilisons pas le nom pour l'appeler! Les fonctions peuvent être anonymes.

Ex. 4.

var message = 'SO';

(function (msg) {   // remove unnecessary reference to foo
    alert(msg);
})(message);

Pour répondre à votre question, reportez-vous à l'exemple 2. Votre première ligne déclare une fonction sans nom et la regroupe, mais ne l'appelle pas. La deuxième ligne regroupe une chaîne. Les deux ne font rien. (Premier exemple de Vincent.)

(function (msg){alert(msg)});  
('SO');                       // nothing.

(foo); 
(msg); //Still nothing.

Mais

(foo)
(msg); //works
Benxamin
la source
6
Merci. Vos exemples étaient assez clairs. Je ne savais pas que les parenthèses en JavaScript pouvaient changer la signification du code de cette façon. Je viens d'un arrière-plan Java, donc j'apprends quelque chose de nouveau (et souvent inattendu) sur JavaScript presque chaque jour où je l'utilise.
hotshot309
5
Merci de l'avoir fait étape par étape, c'est bien mieux que toute autre explication que j'ai vue. +1
Wk_of_Angmar
2
Moment AHA majeur ici - et merci pour l'illustration avec substitution. +100
FredTheWebGuy
1
L'une des meilleures explications que j'ai lues sur les fonctions anonymes. Merci beaucoup!
Teknotica
23

Une fonction anonyme n'est pas une fonction avec le nom "". C'est simplement une fonction sans nom.

Comme toute autre valeur en JavaScript, une fonction n'a pas besoin d'un nom pour être créée. Bien qu'il soit beaucoup plus utile de le lier à un nom comme n'importe quelle autre valeur.

Mais comme toute autre valeur, vous souhaitez parfois l'utiliser sans la lier à un nom. C'est le modèle auto-invocateur.

Voici une fonction et un nombre, non liés, ils ne font rien et ne peuvent jamais être utilisés:

function(){ alert("plop"); }
2;

Nous devons donc les stocker dans une variable pour pouvoir les utiliser, comme toute autre valeur:

var f = function(){ alert("plop"); }
var n = 2;

Vous pouvez également utiliser du sucre syntaxique pour lier la fonction à une variable:

function f(){ alert("plop"); }
var n = 2;

Mais si les nommer n'est pas requis et entraînerait plus de confusion et moins de lisibilité, vous pouvez simplement les utiliser immédiatement.

(function(){ alert("plop"); })(); // will display "plop"
alert(2 + 3); // will display 5

Ici, ma fonction et mes nombres ne sont pas liés à une variable, mais ils peuvent toujours être utilisés.

Dit comme ça, on dirait que la fonction auto-invoquante n'a aucune valeur réelle. Mais vous devez garder à l'esprit que le délimiteur de portée JavaScript est la fonction et non le bloc ({}).

Ainsi, une fonction auto-invoquante a en fait la même signification qu'un bloc C ++, C # ou Java. Ce qui signifie que la variable créée à l'intérieur ne "fuira" pas en dehors de la portée. Ceci est très utile en JavaScript afin de ne pas polluer la portée globale.

Vincent Robert
la source
Belle publication. Que se passera-t-il alors avec la 'function () {alert ("plop"); } 'quand je l'ai exécuté? Ce sera GC'ed?
palig
2
La fonction () {alert ("plop"); } l'instruction alloue simplement la fonction mais ne l'exécute pas ni ne la lie à une variable. Comme la fonction créée n'est liée à aucune variable, elle sera rapidement GCée.
Vincent Robert
Ce thread SO dépasse la portée de ce dont nous parlons ici, mais il explique les façons de séparer les espaces de noms JavaScript - et comprend des exemples qui utilisent des fonctions auto-appelantes.
hotshot309
19

C'est juste comment JavaScript fonctionne. Vous pouvez déclarer une fonction nommée:

function foo(msg){
   alert(msg);
}

Et appelez ça:

foo("Hi!");

Ou, vous pouvez déclarer une fonction anonyme:

var foo = function (msg) {
    alert(msg);
}

Et appelez ça:

foo("Hi!");

Ou, vous ne pouvez tout simplement jamais lier la fonction à un nom:

(function(msg){
   alert(msg);
 })("Hi!");

Les fonctions peuvent également renvoyer des fonctions:

function make_foo() {
    return function(msg){ alert(msg) };
}

(make_foo())("Hi!");

Cela ne vaut rien que les variables définies avec "var" dans le corps de make_foosoient fermées par chaque fonction retournée par make_foo. Il s'agit d'une fermeture et cela signifie que toute modification apportée à la valeur par une fonction sera visible par une autre.

Cela vous permet d'encapsuler des informations, si vous le souhaitez:

function make_greeter(msg){
    return function() { alert(msg) };
}

var hello = make_greeter("Hello!");

hello();

C'est à quel point presque tous les langages de programmation, à l'exception de Java, fonctionnent.

jrockway
la source
8

Le code que vous montrez,

(function (msg){alert(msg)});
('SO');

se composent de deux déclarations. La première est une expression qui produit un objet fonction (qui sera ensuite récupéré car il n'est pas enregistré). La seconde est une expression qui produit une chaîne. Pour appliquer la fonction à la chaîne, vous devez soit passer la chaîne comme argument à la fonction lors de sa création (que vous montrez également ci-dessus), soit vous devrez réellement stocker la fonction dans une variable, afin de pouvoir l'appliquer ultérieurement, à votre guise. Ainsi:

var f = (function (msg){alert(msg)});
f('SO');

Notez qu'en stockant une fonction anonyme (une fonction lambda) dans une variable, vous lui donnez effectivement un nom. Par conséquent, vous pouvez tout aussi bien définir une fonction régulière:

function f(msg) {alert(msg)};
f('SO');
Stephan202
la source
7

En résumé des commentaires précédents:

function() {
  alert("hello");
}();

lorsqu'il n'est pas affecté à une variable, renvoie une erreur de syntaxe. Le code est analysé comme une instruction de fonction (ou définition), ce qui rend les parenthèses fermantes syntaxiquement incorrectes. L'ajout de parenthèses autour de la partie fonction indique à l'interpréteur (et au programmeur) qu'il s'agit d'une expression de fonction (ou invocation), comme dans

(function() {
  alert("hello");
})();

Il s'agit d'une fonction auto-invoquée, ce qui signifie qu'elle est créée de manière anonyme et s'exécute immédiatement car l'invocation se produit sur la même ligne où elle est déclarée. Cette fonction d'auto-invocation est indiquée avec la syntaxe familière d'appeler une fonction sans argument, plus ajouté entre parenthèses autour du nom de la fonction: (myFunction)();.

Il existe une bonne syntaxe de la fonction JavaScript de discussion SO .

hotshot309
la source
3

Cette réponse n'est pas strictement liée à la question, mais vous pourriez être intéressé de découvrir que ce type de fonctionnalité de syntaxe n'est pas spécifique aux fonctions. Par exemple, nous pouvons toujours faire quelque chose comme ceci:

alert(
    {foo: "I am foo", bar: "I am bar"}.foo
); // alerts "I am foo"

Lié aux fonctions. Comme ce sont des objets, qui héritent de Function.prototype, nous pouvons faire des choses comme:

Function.prototype.foo = function () {
    return function () {
        alert("foo");
    };
};

var bar = (function () {}).foo();

bar(); // alerts foo

Et vous savez, nous n'avons même pas besoin d'entourer les fonctions de parenthèses pour les exécuter. Quoi qu'il en soit, tant que nous essayons d'assigner le résultat à une variable.

var x = function () {} (); // this function is executed but does nothing

function () {} (); // syntax error

Une autre chose que vous pouvez faire avec les fonctions, dès que vous les déclarez, est d'invoquer l' newopérateur sur elles et d'obtenir un objet. Les éléments suivants sont équivalents:

var obj = new function () {
    this.foo = "bar";
};

var obj = {
    foo : "bar"
};
Ionuț G. Stan
la source
3

Il existe une autre propriété JavaScript. Si vous souhaitez appeler la même fonction anonyme de manière récursive.

(function forInternalOnly(){

  //you can use forInternalOnly to call this anonymous function
  /// forInternalOnly can be used inside function only, like
  var result = forInternalOnly();
})();

//this will not work
forInternalOnly();// no such a method exist
Anoop
la source
2
+1 Ajout d'un petit échantillon pour que ce soit plus clair :-) La première fois que je l'ai lu, j'ai dû relire 4 fois.
xanatos
3

Ma compréhension de la question du demandeur est telle que:

Comment fonctionne cette magie:

(function(){}) ('input')   // Used in his example

J'ai peut-être tort. Cependant, la pratique habituelle que les gens connaissent est:

(function(){}('input') )

La raison est telle que JavaScript entre parenthèses AKA (), ne peut pas contenir d'instructions et lorsque l'analyseur rencontre le mot-clé de fonction, il sait l'analyser en tant qu'expression de fonction et non en tant que déclaration de fonction.

Source: article de blog Expression de la fonction immédiatement invoquée (IIFE)

laycat
la source
3

exemples sans crochets:

void function (msg) { alert(msg); }
('SO');

(c'est la seule utilisation réelle de void, afaik)

ou

var a = function (msg) { alert(msg); }
('SO');

ou

!function (msg) { alert(msg); }
('SO');

travailler aussi. le voidfait évaluer l'expression, ainsi que l'affectation et le coup. le dernier travaille avec ~, +, -, delete, typeof, certains opérateurs unaires ( voidest un aussi). ne travaillent pas sont de couse ++, en --raison de l'exigence d'une variable.

le saut de ligne n'est pas nécessaire.

Nina Scholz
la source
@Bergi sur ie11 deletefonctionne. même avec 'use strict';. cela fonctionne aussi:delete (3 + 4);
Nina Scholz
Oups, mon erreur. " 2) Si Type (ref) n'est pas Référence, retournez true. " Il ne renvoie des erreurs que pour les références réelles qui ne peuvent pas être résolues.
Bergi
1

Il s'agit d'une fonction anonyme auto-exécutable. Le premier ensemble de crochets contient les expressions à exécuter et le deuxième ensemble de crochets exécute ces expressions.

(function () {
    return ( 10 + 20 );
})();

Peter Michaux discute de la différence dans une importante paire de parenthèses .

Il s'agit d'une construction utile lorsque vous essayez de masquer des variables de l'espace de noms parent. Tout le code de la fonction est contenu dans la portée privée de la fonction, ce qui signifie qu'il n'est pas du tout accessible de l'extérieur de la fonction, ce qui la rend vraiment privée.

Voir:

  1. Clôture (informatique)
  2. Espacement de noms JavaScript
  3. Paire importante de parenthèses Javascript
Ashwin Parmar
la source
0

Un autre point de vue

Tout d'abord, vous pouvez déclarer une fonction anonyme:

var foo = function(msg){
 alert(msg);
}

Ensuite, vous l'appelez:

foo ('Few');

Parce que foo = function (msg) {alert (msg);} vous pouvez donc remplacer foo comme:

function(msg){
 alert(msg);
} ('Few');

Mais vous devez encapsuler l'intégralité de votre fonction anonyme dans une paire d'accolades pour éviter une erreur de syntaxe lors de la déclaration de la fonction lors de l'analyse. Ensuite nous avons,

(function(msg){
 alert(msg);
}) ('Few');

De cette façon, c'est facile à comprendre pour moi.

capu
la source
0

Quand vous l'avez fait:

(function (msg){alert(msg)});
('SO');

Vous avez terminé la fonction avant à ('SO')cause du point-virgule. Si vous écrivez juste:

(function (msg){alert(msg)})
('SO');

Ça va marcher.

Exemple de travail: http://jsfiddle.net/oliverni/dbVjg/

Oliver Ni
la source
0

La simple raison pour laquelle cela ne fonctionne pas n'est pas due à l' ;indication de la fin de la fonction anonyme. C'est parce que sans la ()fin d'un appel de fonction, ce n'est pas un appel de fonction. C'est,

function help() {return true;}

Si vous appelez result = help();c'est un appel à une fonction et retournera vrai.

Si vous appelez, result = help;ce n'est pas un appel. Il s'agit d'une affectation dans laquelle l'aide est traitée comme des données à affecter au résultat.

Ce que vous avez fait était de déclarer / instancier une fonction anonyme en ajoutant le point-virgule,

(function (msg) { /* Code here */ });

puis j'ai essayé de l'appeler dans une autre instruction en utilisant juste des parenthèses ... Évidemment parce que la fonction n'a pas de nom, mais cela ne fonctionnera pas:

('SO');

L'interprète voit les parenthèses sur la deuxième ligne comme une nouvelle instruction / instruction, et donc cela ne fonctionne pas, même si vous l'avez fait comme ceci:

(function (msg){/*code here*/});('SO');

Cela ne fonctionne toujours pas, mais cela fonctionne lorsque vous supprimez le point-virgule car l'interpréteur ignore les espaces blancs et les chariots et voit le code complet comme une seule instruction.

(function (msg){/*code here*/})        // This space is ignored by the interpreter
('SO');

Conclusion: un appel de fonction n'est pas un appel de fonction sans la ()fin sauf si dans des conditions spécifiques telles que l'appel d'une autre fonction, c'est-à-dire que onload = 'help' exécuterait la fonction d'aide même si les parenthèses n'étaient pas incluses. Je crois que setTimeout et setInterval autorisent également ce type d'appel de fonction, et je crois également que l'interprète ajoute de toute façon les parenthèses en arrière-plan, ce qui nous ramène à "un appel de fonction n'est pas un appel de fonction sans les parenthèses".

techniques embauchées
la source
Je ne comprends pas pourquoi cela a reçu autant de votes négatifs. Je pense que c'est une réponse acceptable? : /
Daniel Cheung
0
(function (msg){alert(msg)})
('SO');

Il s'agit d'une méthode courante d'utilisation d'une fonction anonyme comme fermeture que de nombreux frameworks JavaScript utilisent.

Cette fonction est appelée automatiquement lorsque le code est compilé.

S'il est placé ;à la première ligne, le compilateur l'a traité comme deux lignes différentes. Vous ne pouvez donc pas obtenir les mêmes résultats que ci-dessus.

Cela peut également s'écrire:

(function (msg){alert(msg)}('SO'));

Pour plus de détails, consultez les fonctions JavaScript / anonymes .

user2349539
la source
Pour autant que je sache, JavaScript ne "compile" pas
Daniel Cheung
0
  1. Les fonctions anonymes sont des fonctions qui sont déclarées dynamiquement lors de l'exécution. Elles sont appelées fonctions anonymes car elles ne reçoivent pas de nom de la même manière que les fonctions normales.

    Les fonctions anonymes sont déclarées à l'aide de l'opérateur de fonction au lieu de la déclaration de fonction. Vous pouvez utiliser l'opérateur de fonction pour créer une nouvelle fonction partout où il est valide pour mettre une expression. Par exemple, vous pouvez déclarer une nouvelle fonction en tant que paramètre d'un appel de fonction ou attribuer une propriété d'un autre objet.

    Voici un exemple typique d'une fonction nommée:

    fonction flyToTheMoon () {alert ("Zoom! Zoom! Zoom!"); } Voler vers la lune(); Voici le même exemple créé en tant que fonction anonyme:

    var flyToTheMoon = function () {alert ("Zoom! Zoom! Zoom!"); } Voler vers la lune();

    Pour plus de détails, veuillez lire ici:

    http://helephant.com/2008/08/23/javascript-anonymous-functions/

Harikesh Yadav
la source
0

L'IIFE compartimente simplement la fonction et masque la msgvariable afin de ne pas «polluer» l'espace de noms global. En réalité, restez simple et faites comme ci-dessous, sauf si vous créez un site Web d'un milliard de dollars.

var msg = "later dude";
window.onunload = function(msg){
  alert( msg );
};

Vous pouvez nommer votre msgpropriété en utilisant un modèle de module révélateur comme:

var myScript = (function() {
    var pub = {};
    //myscript.msg
    pub.msg = "later dude";
    window.onunload = function(msg) {
        alert(msg);
    };
    //API
    return pub;
}());
Ronnie Royston
la source
-1

Les fonctions anonymes sont censées être des transactions ponctuelles où vous définissez une fonction à la volée afin qu'elle génère une sortie de votre part à partir d'une entrée que vous fournissez. Sauf que vous n'avez pas fourni d'entrée. Au lieu de cela, vous avez écrit quelque chose sur la deuxième ligne («SO»); - une déclaration indépendante qui n'a rien à voir avec la fonction. Qu'est-ce que vous attendiez? :)

Vietnhi Phuvan
la source
Pas 100% correct. Ceci est une fonction anonyme aussi bien et est destiné à être réutilisé: var foo = function() {};. Mais tout le reste va bien.
Felix Kling