Question Quel est le but de l'emballage de fichiers Javascript entiers dans des fonctions anonymes comme "(function () {...}) ()"?


J'ai lu beaucoup de Javascript ces derniers temps et j'ai remarqué que tout le fichier est enveloppé comme suit dans les fichiers .js à importer.

(function() {
    ... 
    code
    ...
})();

Quelle est la raison de faire cela plutôt qu'un simple ensemble de fonctions du constructeur?


525
2018-03-11 01:20


origine


Réponses:


C'est généralement à namespace (voir plus loin) et contrôlez la visibilité des fonctions membres et / ou des variables. Pensez-y comme une définition d'objet. Les plugins jQuery sont généralement écrits comme ça.

En Javascript, vous pouvez imbriquer des fonctions. Donc, ce qui suit est légal:

function outerFunction() {
   function innerFunction() {
      // code
   }
}

Vous pouvez maintenant appeler outerFunction(), mais la visibilité de innerFunction() est limité à la portée de outerFunction(), ce qui signifie qu'il est privé de outerFunction(). Il suit fondamentalement le même principe que les variables en Javascript:

var globalVariable;

function someFunction() {
   var localVariable;
}

Correspondant:

function globalFunction() {

   var localFunction1 = function() {
       //I'm anonymous! But localFunction1 is a reference to me!
   };

   function localFunction2() {
      //I'm named!
   }
}

Dans le scénario ci-dessus, vous pouvez appeler globalFunction() de partout, mais vous ne pouvez pas appeler localFunction1 ou localFunction2.

Ce que tu fais quand tu écris (function() { ... code ... })(), est-ce que vous faites le code à l'intérieur d'une fonction littérale (ce qui signifie que l'ensemble de "l'objet" est en fait une fonction). Après cela, vous invoquez la fonction (la dernière ()). Donc, le principal avantage de ceci, comme je l'ai mentionné précédemment, est que vous pouvez avoir des méthodes / fonctions et propriétés privées:

(function() {
   var private_var;

   function private_function() {
     //code
   }
})()

Dans le premier exemple, globalFunction () était la fonction publique qui pouvait être appelée pour accéder à la fonctionnalité publique, mais dans l'exemple ci-dessus comment l'appelez-vous? Ici, la fonction auto-invoquante fait exécuter automatiquement le code au démarrage. Tout comme vous pouvez ajouter initMyStuff (); En haut de tout fichier .js et il s'exécutera automatiquement dans le cadre de la portée globale, cette fonction auto-invoquée s'exécutera automatiquement, bien que comme il s'agit d'une fonction non nommée, elle ne peut pas être appelée plusieurs fois comme initMyStuff ().

La bonne chose est que vous pouvez également définir des choses à l'intérieur et l'exposer au monde extérieur (un exemple de namespacing afin que vous puissiez créer votre propre bibliothèque / plugin):

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

 return {
    public_function1: function() {
    },
    public_function2: function() {
    }
 }
})()

Vous pouvez maintenant appeler myPlugin.public_function1(), mais vous ne pouvez pas accéder private_function()! Donc assez similaire à une définition de classe. Pour mieux comprendre cela, je recommande les liens suivants pour une lecture plus approfondie:

MODIFIER

J'ai oublié de mentionner. Dans cette finale (), vous pouvez passer tout ce que vous voulez à l'intérieur. Par exemple, lorsque vous créez des plugins jQuery, vous passez jQuery ou $ ainsi:

(function(jQ) { ... code ... })(jQuery) 

Donc ce que vous faites ici est de définir une fonction qui prend un paramètre (appelé jQ, une variable locale et connue seulement à cette fonction). Ensuite, vous invoquez la fonction et passez un paramètre (également appelé jQuery, mais ce l'un vient du monde extérieur et fait référence au jQuery lui-même). Il n'y a pas de besoin urgent de le faire, mais il y a quelques avantages:

  • Vous pouvez redéfinir un paramètre global et lui donner un nom qui a du sens dans la portée locale.
  • Il y a un léger avantage en termes de performances, car il est plus rapide de rechercher des éléments dans la portée locale au lieu d'avoir à passer la chaîne de portée dans la portée globale.
  • Il y a des avantages pour la compression (minification).

Auparavant, j'ai décrit comment ces fonctions s'exécutent automatiquement au démarrage, mais si elles s'exécutent automatiquement qui passe dans les arguments? Cette technique suppose que tous les paramètres sont définis en tant que variables globales. Donc, si jQuery n'était pas défini comme une variable globale, cet exemple ne fonctionnerait pas et ne pourrait pas être appelé d'une autre manière puisque notre exemple est une fonction anonyme. Comme vous pouvez le deviner, une chose que fait jquery.js lors de son initialisation est de définir une variable globale 'jQuery', ainsi que sa variable globale '$' plus célèbre, qui permet à ce code de fonctionner après l'inclusion de jquery.js.


727
2018-03-11 01:32



En bref

Résumé

Dans sa forme la plus simple, cette technique vise à emballer le code dans un portée de la fonction.

Cela aide à diminuer les chances de:

  • entrer en conflit avec d'autres applications / bibliothèques
  • portée polluante (mondiale la plus probable)

Il ne fait pas détecter quand le document est prêt - ce n'est pas une sorte de document.onload ni window.onload

Il est communément connu sous le nom de Immediately Invoked Function Expression (IIFE) ou Self Executing Anonymous Function.

Code expliqué

var someFunction = function(){ console.log('wagwan!'); };

(function() {                   /* function scope starts here */
  console.log('start of IIFE');

  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();                           /* function scope ends */

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Dans l'exemple ci-dessus, toute variable définie dans la fonction (c'est-à-dire déclarée var) sera "privé" et accessible dans le cadre de la fonction SEULEMENT (comme le dit Vivin Paliath). En d'autres termes, ces variables ne sont pas visibles / accessibles en dehors de la fonction. Voir la démo en direct.

Javascript a une portée de fonction. "Les paramètres et les variables définis dans une fonction ne sont pas visibles en dehors de la fonction, et une variable définie n'importe où dans une fonction est visible partout dans la fonction." (à partir de "Javascript: Les bonnes parties").


Plus de détails

Autre code

En fin de compte, le code affiché avant pourrait également être fait comme suit:

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
};

myMainFunction();          // I CALL "myMainFunction" FUNCTION HERE
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Voir la démo en direct.


Les racines

Itération 1

Un jour, quelqu'un a probablement pensé "il doit y avoir un moyen d'éviter de nommer" myMainFunction ", puisque tout ce que nous voulons, c'est l'exécuter immédiatement."

Si vous revenez à l'essentiel, vous découvrez que:

  • expression: quelque chose qui évalue à une valeur. c'est à dire. 3+11/x
  • statement: ligne (s) de code faisant quelque chose MAIS il fait ne pas évaluer à une valeur. c'est à dire. if(){}

De même, les expressions de fonction évaluent une valeur. Et une conséquence (je suppose?) Est qu'ils peuvent être immédiatement invoqués:

 var italianSayinSomething = function(){ console.log('mamamia!'); }();

Donc, notre exemple plus complexe devient:

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
}();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Voir la démo en direct.

Itération 2

L'étape suivante est la pensée "pourquoi avoir var myMainFunction = si nous ne l'utilisons même pas !?

La réponse est simple: essayez de supprimer ceci, comme ci-dessous:

 function(){ console.log('mamamia!'); }();

Voir la démo en direct.

Ça ne marchera pas parce que "les déclarations de fonction ne sont pas invocables".

Le truc est qu'en enlevant var myMainFunction = nous avons transformé le expression de fonction dans une déclaration de fonction. Voir les liens dans "Ressources" pour plus de détails à ce sujet.

La question suivante est "pourquoi je ne peux pas le garder comme une expression de fonction avec autre chose que var myMainFunction =?

La réponse est "vous pouvez", et il y a plusieurs façons de le faire: ajouter un +, une !, une -, ou peut-être envelopper dans une paire de parenthèses (comme c'est maintenant fait par convention), et plus je crois. Comme exemple:

 (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.

ou

 +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console

ou

 -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console

Donc, une fois que la modification pertinente est ajoutée à ce qui était autrefois notre «code alternatif», nous revenons exactement au même code que celui utilisé dans l'exemple «Code expliqué»

var someFunction = function(){ console.log('wagwan!'); };

(function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

En savoir plus sur Expressions vs Statements:


Scopes démystifier

Une chose que l'on pourrait se demander est "que se passe-t-il lorsque vous ne définissez PAS la variable" correctement "à l'intérieur de la fonction - c'est-à-dire que vous faites une simple assignation à la place?"

(function() {
  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  myOtherFunction = function(){  /* oops, an assignment instead of a declaration */
    console.log('haha. got ya!');
  };
})();
myOtherFunction();         // reachable, hence works: see in the console
window.myOtherFunction();  // works in the browser, myOtherFunction is then in the global scope
myFunction();              // unreachable, will throw an error, see in the console

Voir la démo en direct.

Fondamentalement, si une variable qui n'a pas été déclarée dans sa portée actuelle est affectée d'une valeur, alors "une recherche de la chaîne de portée se produit jusqu'à ce qu'elle trouve la variable ou frappe la portée globale (à quel point elle la créera)".

Dans un environnement de navigateur (par rapport à un environnement de serveur comme nodejs), la portée globale est définie par le window objet. D'où nous pouvons faire window.myOtherFunction().

Mon conseil "Bonnes pratiques" sur ce sujet est de toujours utiliser var en définissant quelque chose: qu'il s'agisse d'un nombre, d'un objet ou d'une fonction, et même s'il se trouve dans la portée globale. Cela rend le code beaucoup plus simple.

Remarque:

  • le javascript ne pas avoir block scope (Mise à jour: bloquez les variables locales d'étendue ajoutées dans ES6.)
  • javascript a seulement function scope & global scope (window portée dans un environnement de navigateur)

En savoir plus sur Javascript Scopes:


Ressources


Prochaines étapes

Une fois que vous obtenez ceci IIFE concept, il conduit à la module pattern, ce qui est généralement fait en tirant parti de ce modèle IIFE. S'amuser :)


75
2017-11-04 15:31



Javascript dans un navigateur n'a vraiment qu'un couple d'étendues efficaces: la portée de la fonction et la portée globale.

Si une variable n'est pas dans la portée de la fonction, elle est globale. Et les variables globales sont généralement mauvaises, donc c'est une construction pour garder les variables d'une bibliothèque à elle-même.


26
2018-03-11 01:25



C'est ce qu'on appelle une fermeture. Il scelle fondamentalement le code à l'intérieur de la fonction afin que les autres bibliothèques ne l'interfèrent pas. C'est similaire à la création d'un espace de noms dans les langages compilés.

Exemple. Supposons que j'écris:

(function() {

    var x = 2;

    // do stuff with x

})();

Maintenant, les autres bibliothèques ne peuvent pas accéder à la variable x J'ai créé pour utiliser dans ma bibliothèque.


19
2018-03-11 01:25



Vous pouvez utiliser les fermetures de fonction Les données dans les expressions plus grandes, comme dans cette méthode de détermination du support du navigateur pour certains des objets html5.

   navigator.html5={
     canvas: (function(){
      var dc= document.createElement('canvas');
      if(!dc.getContext) return 0;
      var c= dc.getContext('2d');
      return typeof c.fillText== 'function'? 2: 1;
     })(),
     localStorage: (function(){
      return !!window.localStorage;
     })(),
     webworkers: (function(){
      return !!window.Worker;
     })(),
     offline: (function(){
      return !!window.applicationCache;
     })()
    }

8
2018-03-11 02:59



En plus de garder les variables locales, une utilisation très pratique est lors de l'écriture d'une bibliothèque en utilisant une variable globale, vous pouvez lui donner un nom de variable plus court à utiliser dans la bibliothèque. Il est souvent utilisé pour écrire des plugins jQuery, puisque jQuery vous permet de désactiver la variable $ pointant vers jQuery, en utilisant jQuery.noConflict (). Au cas où il est désactivé, votre code peut toujours utiliser $ et ne pas casser si vous faites juste:

(function($) { ...code...})(jQuery);

7
2018-03-11 01:33



  1. Pour éviter le conflit avec d'autres méthodes / bibliothèques dans la même fenêtre,
  2. Eviter la portée globale, en faire une portée locale,
  3. Pour accélérer le débogage (portée locale),
  4. JavaScript a uniquement une portée de fonction, donc cela aidera aussi à la compilation de codes.

3
2017-10-26 18:52



Nous devrions également utiliser 'use strict' dans la fonction scope pour nous assurer que le code devrait être exécuté en "mode strict". Exemple de code ci-dessous

(function() {
    'use strict';

    //Your code from here
})();

1
2017-11-24 06:37