À quoi sert une fonction auto-exécutable en javascript?

427

En javascript, quand voudriez-vous utiliser ceci:

(function(){
    //Bunch of code...
})();

sur ceci:

//Bunch of code...
Ej.
la source
3
Consultez également une explication ( technique ) et ici . Pour la syntaxe, voyez pourquoi les parenthèses sont nécessaires et où elles doivent aller .
Bergi
Pourquoi a-t-il les deux dernières parenthèses, juste avant le point-virgule?
johnny
3
@johnny la partie avant ces deux dernières parenthèses déclare une fonction (anonyme). Ces deux parenthèses appellent la fonction.
Ej.
7
«Expression de fonction immédiatement invoquée» ou IIFE est un meilleur nom pour cela.
Flimm

Réponses:

404

C'est une question de portée variable. Les variables déclarées dans la fonction auto-exécutable sont, par défaut, uniquement disponibles pour coder dans la fonction auto-exécutable. Cela permet d'écrire du code sans se soucier de la façon dont les variables sont nommées dans d'autres blocs de code JavaScript.

Par exemple, comme mentionné dans un commentaire d' Alexandre :

(function() {
  var foo = 3;
  console.log(foo);
})();

console.log(foo);

Cela enregistrera d'abord 3et ensuite lancera une erreur sur le suivant console.logcar foon'est pas défini.

Ken Browning
la source
7
Et aussi pour le bénéfice de nombreuses personnes, y compris un tas d'ingénieurs Netflix: C'EST JUSTE UNE FONCTION. Il n'est pas en soi représentatif d'une fermeture. Parfois, les invocateurs automatiques sont utilisés en conjonction avec des scénarios pertinents pour la fermeture pour faire des trucs soignés, mais si vous ne voyez pas quelque chose s'accrocher à une référence qui serait récupérée et utilisée dans un langage sans fermeture, cela n'a rien à flipper faire avec des FERMETURES.
Erik Reppen
2
Donc, cela signifie, son principalement utilisé avec fermeture?
Pir Abdul
@AlexanderBird, pas tout à fait raison ... si vous le faites sans var, comme ceci: ...function(){ foo=3;}? Cela définirait une variable globale, non?
T.Todua
2
@AlexanderBird mais cela se produit déjà dans les variables locales à l'intérieur des fonctions: function(){ var foo = 3; alert(foo); }; alert(foo);donc je ne comprends toujours pas
João Pimentel Ferreira
Ah, je comprends, vous obtenez toutes ces 3 fonctionnalités en même temps: 1) vous exécutez la fonction en la déclarant seulement; 2) Comme toute fonction, la portée des variables est uniquement locale; et 3) La fonction peut être anonyme, ne polluant pas la portée principale
João Pimentel Ferreira
94

Simpliste. Donc très normal, c'est presque réconfortant:

var userName = "Sean";

console.log(name());

function name() {
  return userName;
}

Cependant, que se passe-t-il si j'inclus une bibliothèque javascript très pratique dans ma page qui traduit les caractères avancés dans leurs représentations de niveau de base?

Attends quoi?

Je veux dire, si quelqu'un tape un caractère avec une sorte d'accent dessus, mais je veux seulement des caractères 'anglais' AZ dans mon programme? Eh bien ... les caractères espagnol «ñ» et français «é» peuvent être traduits en caractères de base «n» et «e».

Donc, quelqu'un de gentil a écrit un convertisseur de caractères complet que je peux inclure dans mon site ... Je l'inclus.

Un problème: il contient une fonction appelée 'nom' identique à ma fonction.

C'est ce qu'on appelle une collision. Nous avons deux fonctions déclarées dans la même portée avec le même nom. Nous voulons éviter cela.

Nous devons donc en quelque sorte étendre notre code.

La seule façon d'étendre le code en javascript est de l'envelopper dans une fonction:

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

Cela pourrait résoudre notre problème. Tout est désormais fermé et n'est accessible qu'à partir de nos accolades d'ouverture et de fermeture.

Nous avons une fonction dans une fonction ... ce qui est bizarre à regarder, mais totalement légal.

Un seul problème. Notre code ne fonctionne pas. Notre variable userName n'est jamais répercutée dans la console!

Nous pouvons résoudre ce problème en ajoutant un appel à notre fonction après notre bloc de code existant ...

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

main();

Ou avant!

main();

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

Une préoccupation secondaire: quelles sont les chances que le nom «principal» n'ait pas encore été utilisé? ... donc très, très mince.

Nous avons besoin de PLUS de portée. Et un moyen d'exécuter automatiquement notre fonction main ().

Nous arrivons maintenant aux fonctions d'auto-exécution (ou auto-exécutables, auto-exécutables, peu importe).

((){})();

La syntaxe est maladroite comme péché. Mais ça marche.

Lorsque vous enveloppez une définition de fonction entre parenthèses et incluez une liste de paramètres (un autre ensemble ou des parenthèses!), Elle agit comme un appel de fonction .

Regardons donc à nouveau notre code, avec une syntaxe auto-exécutable:

(function main() {
  var userName = "Sean";

    console.log(name());

    function name() {
      return userName;
    }
  }
)();

Ainsi, dans la plupart des didacticiels que vous lisez, vous serez désormais bombardé par le terme «auto-exécution anonyme» ou quelque chose de similaire.

Après de nombreuses années de développement professionnel, je vous invite fortement à nommer chaque fonction que vous écrivez à des fins de débogage.

Quand quelque chose ne va pas (et ça arrivera), vous vérifierez la trace dans votre navigateur. Il est toujours plus facile d'affiner vos problèmes de code lorsque les entrées de la trace de pile ont des noms!

Énorme longue haleine et j'espère que ça aide!

Sean Holden
la source
2
Merci :) Je cherchais sur Internet tout autour pour essayer de comprendre les avantages de l'IIFE par rapport aux fonctions normales en termes de confidentialité variable et votre réponse est tout simplement la meilleure. Tout le monde dit que l'un des meilleurs avantages est que les variables et les fonctions à l'intérieur de IIFE sont «finalement» privées lorsqu'une fonction normale vous donne exactement la même chose. Enfin, je pense que j'ai compris votre processus d'explication. L'IIFE n'est qu'une fonction après tout, mais maintenant je comprends pourquoi l'utiliser, merci encore!
viery365
Merci d'avoir pris le temps de l'expliquer si bien.
MSC
Bonne réponse. J'ai une question sur votre dernier point, cependant - Lorsque vous recommandez que toutes les fonctions soient nommées, dites-vous qu'il existe un moyen de le faire avec des fonctions auto-exécutables, ou suggérez-vous que tout le monde nomme la fonction puis l'appelle? EDIT Oh, je vois. Celui-ci est déjà nommé. Duh. Je voudrais peut-être souligner que vous justifiez votre utilisation d'une fonction auto-exécutable nommée.
FireSBurnsmuP
Eh bien mon ami, c'est LA réponse que je cherchais:)
João Pimentel Ferreira
C'est la réponse que je cherchais, pas celle qui est marquée comme acceptée. Ai-je raison de dire qu'il ne s'agit pas d'une collision de noms de variables , mais d'une collision de noms de fonctions ? Les variables, même dans les fonctions non iife normales, ont une portée locale et n'entrent pas en collision avec des variables globales, n'est-ce pas?
Kumar Manish
32

L'auto-invocation (également appelée auto-invocation) se produit lorsqu'une fonction s'exécute immédiatement après sa définition. Il s'agit d'un modèle de base et sert de base à de nombreux autres modèles de développement JavaScript.

J'en suis un grand fan :) car:

  • Il réduit le code au minimum
  • Il impose la séparation du comportement de la présentation
  • Il fournit une fermeture qui empêche les conflits de nommage

Énormément - (Pourquoi devriez-vous dire que c'est bon?)

  • Il s'agit de définir et d'exécuter une fonction à la fois.
  • Vous pouvez demander à cette fonction auto-exécutable de renvoyer une valeur et de transmettre la fonction en tant que paramètre à une autre fonction.
  • C'est bon pour l'encapsulation.
  • Il est également bon pour l'étendue des blocs.
  • Oui, vous pouvez inclure tous vos fichiers .js dans une fonction auto-exécutable et empêcher la pollution globale de l'espace de noms. ;)

Plus ici .

MA Hossain Tonu
la source
43
Point 1. Comment? Point 2. C'est à partir d'une meilleure pratique complètement différente. Point 3. Quelle fonction ne fonctionne pas? 4,5,6,7. Pertinence? 8. Eh bien, 1/8 n'est pas mal, je suppose.
Erik Reppen
2
Sept ans de retard mais, pour le point 1. cela ne réduit pas du tout le code, en fait il ajoute un minimum de deux lignes de code dans la création de la fonction.
YungGun
1
le seul point ici est "Il fournit une fermeture qui empêche les conflits de nommage", tout autre point est reformulé ou faux. peut-être pouvez-vous simplifier votre réponse?
pcarvalho
19

Espace de noms. Les étendues de JavaScript sont au niveau de la fonction.

Christoph
la source
7
les votes vers le bas arrivent toujours parce que j'ai utilisé l' espace de noms au lieu de la portée ; c'est une question de définition - voir par exemple Wikipedia : Un espace de noms en informatique (parfois aussi appelé portée de nom), est un conteneur ou un environnement abstrait créé pour contenir un regroupement logique d'identificateurs ou de symboles uniques (c.-à-d. des noms). et un identificateur d'espace de noms peut fournir un contexte (Champ d' application de la science informatique) à un nom, et les termes sont parfois utilisés de façon interchangeable.
Christoph
5
Les étendues de niveau fonction de Javascript fournissent l'espace dans lequel vivent les noms de variables, un espace de noms ; qu'il ne s'agit pas d'un identifiant anonyme associé à un identifiant d'espace de nom est sans importance ...
Christoph
12

Je ne peux pas croire qu'aucune des réponses ne mentionne des globaux implicites.

La (function(){})()construction ne protège pas contre les globaux implicites, ce qui pour moi est la plus grande préoccupation, voir http://yuiblog.com/blog/2006/06/01/global-domination/

Fondamentalement, le bloc fonction garantit que tous les "variables globales" dépendantes que vous avez définies sont confinés à votre programme, il ne vous protège pas contre la définition de globaux implicites. JSHint ou similaire peut fournir des recommandations sur la façon de se défendre contre ce comportement.

La var App = {}syntaxe plus concise offre un niveau de protection similaire et peut être encapsulée dans le bloc fonction sur les pages «publiques». (voir Ember.js ou SproutCore pour des exemples réels de bibliothèques qui utilisent cette construction)

En ce qui concerne les privatepropriétés, elles sont en quelque sorte surévaluées, sauf si vous créez un cadre public ou une bibliothèque, mais si vous devez les implémenter, Douglas Crockford a de bonnes idées.

David W. Keith
la source
8
Le mode strict protège contre les globaux implicites. En conjonction avec un auto-invocateur, vous auriez couvert. Je n'ai jamais compris le hooplah sur les propriétés privées. Déclarez vars à l'intérieur d'un constructeur func. Terminé. Si l'idée d'oublier d'utiliser le mot-clé «nouveau» vous empêche de dormir la nuit, écrivez une fonction d'usine. Refait.
Erik Reppen
8

J'ai lu toutes les réponses, il manque quelque chose de très important ici , je vais BAISER. Il y a 2 raisons principales pour lesquelles j'ai besoin de fonctions anonymes auto-exécutables, ou mieux dit " expression de fonction immédiatement invoquée (IIFE) ":

  1. Meilleure gestion des espaces de noms (éviter la pollution des espaces de noms -> module JS)
  2. Fermetures (simulation de membres d'une classe privée, comme le sait la POO)

Le premier a été très bien expliqué. Pour le second, veuillez étudier l'exemple suivant:

var MyClosureObject = (function (){
  var MyName = 'Michael Jackson RIP';
  return {
    getMyName: function () { return MyName;},
    setMyName: function (name) { MyName = name}
  }
}());

Attention 1: Nous n'affectons pas de fonction à MyClosureObject, plus encore le résultat de l'invocation de cette fonction . Soyez conscient de ()la dernière ligne.

Attention 2: Ce que vous devez en plus savoir sur les fonctions en Javascript, c'est que les fonctions internes ont accès aux paramètres et variables des fonctions, elles sont définies à l'intérieur.

Essayons quelques expériences:

Je peux MyNameutiliser getMyNameet cela fonctionne:

 console.log(MyClosureObject.getMyName()); 
 // Michael Jackson RIP

L'approche ingénue suivante ne fonctionnerait pas:

console.log(MyClosureObject.MyName); 
// undefined

Mais je peux définir un autre nom et obtenir le résultat attendu:

MyClosureObject.setMyName('George Michael RIP');
console.log(MyClosureObject.getMyName()); 
// George Michael RIP

Edit: dans l'exemple ci MyClosureObject- dessus est conçu pour être utilisé sans le newpréfixe, par conséquent, il ne doit pas être mis en majuscule.

solitaire
la source
7

Existe-t-il un paramètre et le "Bunch of code" renvoie une fonction?

var a = function(x) { return function() { document.write(x); } }(something);

Fermeture. La valeur de somethingest utilisée par la fonction affectée à a. somethingpourrait avoir une valeur variable (pour la boucle) et chaque fois que a a une nouvelle fonction.

stesch
la source
+1; Je préfère une explicite var x = something;dans la fonction extérieure àx que comme paramètre: imo c'est plus lisible de cette façon ...
Christoph
@Christoph: Si la valeur de "quelque chose" change après la création de la fonction, alors il utilisera la nouvelle valeur et non celle au moment de sa création.
stesch
@stesch: d'où avez-vous tiré cela? Pour autant que je sache, ce n'est pas le cas; la seule façon d'obtenir de vraies références dans JS est d'utiliser l'argument-objet, mais même cela ne fonctionne pas dans tous les navigateurs
Christoph
@Christoph: "JavaScript: les bonnes parties", Douglas Crockford (O'Reilly)
stesch
@stesch: cela ne fonctionne pas comme vous le décrivez: la nouvelle valeur sera utilisée si vous supprimez la variable x et dépendez directement de la portée lexicale, c'est-à-dire document.write(something)...
Christoph
6

Isolement de la portée, peut-être. Pour que les variables à l'intérieur de la déclaration de fonction ne polluent pas l'espace de noms externe.

Bien sûr, sur la moitié des implémentations JS, elles le seront de toute façon.

le chaos
la source
4
Quelles implémentations seraient-elles?
Matthew Crumley
1
Toute implémentation non écrite en mode strict et contenant une déclaration var implicite qui la rend globale.
Erik Reppen
5

Voici un exemple solide de l'utilité d'une fonction anonyme auto-invoquante.

for( var i = 0; i < 10; i++ ) {
  setTimeout(function(){
    console.log(i)
  })
}

Production: 10, 10, 10, 10, 10...

for( var i = 0; i < 10; i++ ) {
  (function(num){
    setTimeout(function(){
      console.log(num)
    })
  })(i)
}

Production: 0, 1, 2, 3, 4...

sg.cc
la source
pouvez-vous expliquer un peu plus ce qui se passe pour le premier ensemble de code
radio_head
Avec letau lieu du varpremier cas, ça ira.
Vitaly Zdanevich
3

Une différence est que les variables que vous déclarez dans la fonction sont locales, donc elles disparaissent lorsque vous quittez la fonction et n'entrent pas en conflit avec d'autres variables dans un autre code.

Guffa
la source
1

Puisque les fonctions en Javascript sont un objet de première classe, en le définissant de cette façon, il définit effectivement une "classe" un peu comme C ++ ou C #.

Cette fonction peut définir des variables locales et contenir des fonctions. Les fonctions internes (effectivement les méthodes d'instance) auront accès aux variables locales (effectivement les variables d'instance), mais elles seront isolées du reste du script.

James Curran
la source
1

Fonction auto-invoquée en javascript:

Une expression auto-invoquante est invoquée (démarrée) automatiquement, sans être appelée. Une expression auto-invoquante est invoquée juste après sa création. Ceci est essentiellement utilisé pour éviter les conflits de nommage ainsi que pour réaliser l'encapsulation. Les variables ou objets déclarés ne sont pas accessibles en dehors de cette fonction. Pour éviter les problèmes de minimisation (filename.min), utilisez toujours une fonction auto-exécutée.

Kishor Vitekar
la source
1

Les fonctions auto-exécutables sont utilisées pour gérer la portée d'une variable.

La portée d'une variable est la région de votre programme dans laquelle elle est définie.

Une variable globale a une portée globale; il est défini partout dans votre code JavaScript et est accessible de n'importe où dans le script, même dans vos fonctions. En revanche, les variables déclarées dans une fonction ne sont définies que dans le corps de la fonction. Ce sont des variables locales, ont une portée locale et ne sont accessibles que dans cette fonction. Les paramètres de fonction comptent également comme des variables locales et ne sont définis que dans le corps de la fonction.

Comme indiqué ci-dessous, vous pouvez accéder à la variable variable globale à l'intérieur de votre fonction et notez également que dans le corps d'une fonction, une variable locale a priorité sur une variable globale du même nom.

var globalvar = "globalvar"; // this var can be accessed anywhere within the script

function scope() {
    alert(globalvar);
    localvar = "localvar" //can only be accessed within the function scope
}

scope(); 

Donc, fondamentalement, une fonction auto-exécutable permet d'écrire du code sans se soucier de la façon dont les variables sont nommées dans d'autres blocs de code javascript.

Adeojo Emmanuel IMM
la source
1
(function(){
    var foo = {
        name: 'bob'
    };
    console.log(foo.name); // bob
})();
console.log(foo.name); // Reference error

En fait, la fonction ci-dessus sera traitée comme une expression de fonction sans nom.

L'objectif principal d'encapsuler une fonction avec des parenthèses fermées et ouvertes est d'éviter de polluer l'espace global.

Les variables et les fonctions à l'intérieur de l'expression de fonction sont devenues privées (c'est-à-dire) qu'elles ne seront pas disponibles en dehors de la fonction.

Madhankumar
la source
1

La réponse courte est: pour prévenir la pollution de portée mondiale (ou supérieure).

IIFE (Immediately Invoked Function Expressions) est la meilleure pratique pour écrire des scripts en tant que plug-ins, modules complémentaires, scripts utilisateur ou tout autre script qui devrait fonctionner avec les scripts d'autres personnes . Cela garantit que toute variable que vous définissez ne donne pas d'effets indésirables sur d'autres scripts.

C'est l'autre façon d'écrire l'expression IIFE. Personnellement, je préfère cette méthode suivante:

void function() {
  console.log('boo!');
  // expected output: "boo!"
}();

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

D'après l'exemple ci-dessus, il est très clair que l'IIFE peut également affecter l'efficacité et les performances, car la fonction qui ne doit être exécutée qu'une seule fois sera exécutée une fois, puis transférée dans le vide pour de bon . Cela signifie que la déclaration de fonction ou de méthode ne reste pas en mémoire.

Donovan P
la source
Bien, je n'avais jamais vu cette utilisation voidauparavant. Je l'aime.
Ej.
1

Vous devez d'abord visiter MDN IIFE , Maintenant quelques points à ce sujet

  • c'est l' expression de fonction immédiatement invoquée . Ainsi, lorsque votre fichier javascript est appelé depuis HTML, cette fonction est appelée immédiatement.
  • Cela empêche d'accéder aux variables au sein de l'idiome IIFE ainsi que de polluer la portée globale.
Justin
la source
0

Il semble que cette question ait été répondue toute prête, mais je posterai quand même mon entrée.

Je sais quand j'aime utiliser des fonctions auto-exécutables.

var myObject = {
    childObject: new function(){
        // bunch of code
    },
    objVar1: <value>,
    objVar2: <value>
}

La fonction me permet d'utiliser du code supplémentaire pour définir les attributs et les propriétés childObjects pour un code plus propre, comme la définition de variables couramment utilisées ou l'exécution d'équations mathématiques; Oh! ou vérification d'erreur. au lieu d'être limité à la syntaxe d'instanciation d'objets imbriqués de ...

object: {
    childObject: {
        childObject: {<value>, <value>, <value>}
    }, 
    objVar1: <value>,
    objVar2: <value>
}

Le codage en général a de nombreuses façons obscures de faire la même chose, ce qui vous fait vous demander: "Pourquoi s'embêter?" Mais de nouvelles situations continuent de surgir où vous ne pouvez plus compter uniquement sur les principaux de base / principaux.

Garrett
la source
0

Étant donné votre question simple: "En javascript, quand voudriez-vous utiliser ceci: ..."

J'aime les réponses de @ken_browning et @ sean_holding, mais voici un autre cas d'utilisation que je ne vois pas mentionné:

let red_tree = new Node(10);

(async function () {
    for (let i = 0; i < 1000; i++) {
        await red_tree.insert(i);
    }
})();

console.log('----->red_tree.printInOrder():', red_tree.printInOrder());

où Node.insert est une action asynchrone.

Je ne peux pas simplement appeler wait sans le mot clé async lors de la déclaration de ma fonction, et je n'ai pas besoin d'une fonction nommée pour une utilisation ultérieure, mais je dois attendre cet appel d'insertion ou j'ai besoin d'autres fonctionnalités plus riches (qui sait?) .

zentechinc
la source
-3

IIRC il vous permet de créer des propriétés et des méthodes privées.

Ólafur Waage
la source