Pregunta ¿Cuál es el propósito de envolver archivos Javascript completos en funciones anónimas como "(función () {...}) ()"?


He estado leyendo mucho Javascript últimamente y me he dado cuenta de que todo el archivo está empaquetado como el siguiente en los archivos .js que se importan.

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

¿Cuál es la razón para hacer esto en lugar de un simple conjunto de funciones de constructor?


525
2018-03-11 01:20


origen


Respuestas:


Por lo general, es para el espacio de nombres (ver más adelante) y controlar la visibilidad de las funciones y / o variables de los miembros. Piense en ello como una definición de objeto. Los plugins de jQuery generalmente se escriben así.

En Javascript, puedes anidar funciones. Entonces, lo siguiente es legal:

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

Ahora puedes llamar outerFunction(), pero la visibilidad de innerFunction() está limitado al alcance de outerFunction(), lo que significa que es privado outerFunction(). Básicamente sigue el mismo principio que las variables en Javascript:

var globalVariable;

function someFunction() {
   var localVariable;
}

En correspondencia:

function globalFunction() {

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

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

En el escenario anterior, puede llamar globalFunction() desde cualquier lugar, pero no puedes llamar localFunction1 o localFunction2.

Qué estás haciendo cuando escribes (function() { ... code ... })(), está haciendo que el código dentro de una función sea literal (lo que significa que el "objeto" completo es en realidad una función). Después de eso, estás autoinvocando la función (la final ()) Así que la principal ventaja de esto, como mencioné antes, es que puedes tener métodos / funciones y propiedades privadas:

(function() {
   var private_var;

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

En el primer ejemplo, globalFunction () era la función pública a la que se podía llamar para acceder a la funcionalidad pública, pero en el ejemplo anterior ¿cómo se llama? Aquí la función de invocación automática hace que el código se ejecute automáticamente al inicio. Al igual que puede agregar initMyStuff (); en la parte superior de cualquier archivo .js y se ejecutará automáticamente como parte del alcance global, esta función de invocación automática también se ejecutará automáticamente, aunque dado que es una función sin nombre, no se puede invocar varias veces como podría ser initMyStuff ().

Lo bueno es que también puedes definir cosas dentro y exponerlas al mundo exterior (por ejemplo, un espacio de nombres para que puedas crear tu propia biblioteca / complemento):

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

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

Ahora puedes llamar myPlugin.public_function1(), pero no puedes acceder private_function()! Muy similar a una definición de clase. Para entender esto mejor, recomiendo los siguientes enlaces para leer más:

EDITAR

Olvidé mencionar. En esa final (), puedes pasar todo lo que quieras dentro. Por ejemplo, cuando crea los complementos jQuery, pasa jQuery o $ al igual que:

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

Entonces, lo que estás haciendo aquí es definir una función que tome un parámetro (llamado jQ, una variable local, y conocida solamente a esa función). Luego, usted invoca automáticamente la función y pasa un parámetro (también llamado jQuery, pero esta uno es del mundo exterior y una referencia al propio jQuery). No hay una necesidad urgente de hacer esto, pero hay algunas ventajas:

  • Puede redefinir un parámetro global y darle un nombre que tenga sentido en el ámbito local.
  • Hay una ligera ventaja de rendimiento, ya que es más rápido buscar cosas en el ámbito local en lugar de tener que subir la cadena de alcance al ámbito global.
  • Hay beneficios para la compresión (minificación).

Anteriormente describí cómo estas funciones se ejecutan automáticamente al inicio, pero si se ejecutan automáticamente ¿quién está pasando los argumentos? Esta técnica supone que todos los parámetros están definidos como variables globales. Entonces, si jQuery no se definió como una variable global, este ejemplo no funcionaría, y no podría llamarse de ninguna otra forma ya que nuestro ejemplo es una función anónima. Como se puede adivinar, una de las cosas que hace jquery.js durante su inicialización es definir una variable global 'jQuery', así como su variable global '$' más famosa, que permite que este código funcione después de que se incluye jquery.js.


727
2018-03-11 01:32



En breve

Resumen

En su forma más simple, esta técnica tiene como objetivo ajustar el código dentro de un alcance de la función.

Ayuda a disminuir las posibilidades de:

  • chocando con otras aplicaciones / bibliotecas
  • contaminante superior (alcance global más probable)

Eso no detectar cuando el documento está listo, no es un tipo de document.onload ni window.onload

Es comúnmente conocido como Immediately Invoked Function Expression (IIFE) o Self Executing Anonymous Function.

Código explicado

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

En el ejemplo anterior, cualquier variable definida en la función (es decir, declarada usando var) será "privado" y accesible dentro del alcance de la función ÚNICAMENTE (como lo dice Vivin Paliath). En otras palabras, estas variables no son visibles / alcanzables fuera de la función. Ver demostración en vivo.

Javascript tiene función de alcance. "Los parámetros y variables definidos en una función no son visibles fuera de la función, y una variable definida en cualquier lugar dentro de una función es visible en todas partes dentro de la función". (de "Javascript: The Good Parts").


Más detalles

Código alternativo

Al final, el código publicado anteriormente también se puede hacer de la siguiente manera:

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

Ver demostración en vivo.


Las raices

Iteración 1

Un día, alguien probablemente pensó "debe haber una manera de evitar nombrar a 'myMainFunction', ya que todo lo que queremos es ejecutarlo inmediatamente".

Si vuelves a lo básico, descubres que:

  • expression: algo evaluando a un valor. es decir 3+11/x
  • statement: línea (s) de código haciendo algo PERO lo hace no evaluar a un valor. es decir if(){}

Del mismo modo, las expresiones de función se evalúan a un valor. Y una consecuencia (¿supongo?) Es que pueden invocarse inmediatamente:

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

Entonces nuestro ejemplo más complejo se convierte en:

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

Ver demostración en vivo.

Iteración 2

El siguiente paso es el pensamiento "¿por qué var myMainFunction = si ni siquiera lo usamos !? ".

La respuesta es simple: intente eliminar esto, como a continuación:

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

Ver demostración en vivo.

No funcionará porque "declaraciones de funciones no son invocables".

El truco es que al eliminar var myMainFunction = transformamos el expresión de función en un declaración de función. Vea los enlaces en "Recursos" para más detalles sobre esto.

La siguiente pregunta es "¿por qué no puedo mantenerlo como una expresión de función con algo más que var myMainFunction =?

La respuesta es "usted puede", y en realidad hay muchas maneras en que puede hacer esto: agregar un +, un !, un -, o tal vez incluir un par de paréntesis (como ahora se hace por convención), y más, creo. Como ejemplo:

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

o

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

o

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

Entonces, una vez que la modificación relevante se agrega a lo que fue nuestro "Código Alternativo", volvemos al mismo código exacto que el utilizado en el ejemplo "Código Explicado"

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

Leer más sobre Expressions vs Statements:


Desmitificando alcances

Una cosa que uno podría preguntarse es "¿qué sucede cuando NO se define la variable 'correctamente' dentro de la función, es decir, hacer una asignación simple en su lugar?"

(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

Ver demostración en vivo.

Básicamente, si a una variable que no se declaró en su ámbito actual se le asigna un valor, entonces "se busca una cadena de alcance hasta que encuentra la variable o alcanza el alcance global (en cuyo punto lo creará)".

Cuando se encuentra en un entorno de navegador (frente a un entorno de servidor como nodejs), el ámbito global está definido por window objeto. Por lo tanto, podemos hacer window.myOtherFunction().

Mi sugerencia de "Buenas prácticas" sobre este tema es siempre usa var al definir cualquier cosa: si es un número, objeto o función, e incluso cuando está en el alcance global. Esto hace que el código sea mucho más simple.

Nota:

  • javascript lo hace no tener block scope (Actualización: variables locales de ámbito de bloque agregadas en ES6.)
  • javascript tiene solo function scope & global scope (window alcance en un entorno de navegador)

Leer más sobre Javascript Scopes:


Recursos


Próximos pasos

Una vez que obtienes esto IIFE concepto, conduce a la module pattern, que se hace comúnmente aprovechando este patrón IIFE. Que te diviertas :)


75
2017-11-04 15:31



Javascript en un navegador solo tiene un par de ámbitos efectivos: alcance de la función y alcance global.

Si una variable no está en el alcance de la función, está en alcance global. Y las variables globales generalmente son malas, por lo que esta es una construcción para mantener las variables de una biblioteca para sí misma.


26
2018-03-11 01:25



Eso se llama cierre. Básicamente, sella el código dentro de la función para que otras bibliotecas no interfieran con él. Es similar a crear un espacio de nombres en lenguajes compilados.

Ejemplo. Supongamos que escribo:

(function() {

    var x = 2;

    // do stuff with x

})();

Ahora otras bibliotecas no pueden acceder a la variable x Creé para usar en mi biblioteca.


19
2018-03-11 01:25



Puede usar cierres de función como datos en expresiones más grandes también, como en este método de determinar el soporte del navegador para algunos de los objetos 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



Además de mantener las variables locales, un uso muy útil es cuando se escribe una biblioteca usando una variable global, puede darle un nombre de variable más corto para usar dentro de la biblioteca. A menudo se usa para escribir complementos jQuery, ya que jQuery le permite desactivar la variable $ apuntando a jQuery, usando jQuery.noConflict (). En caso de que esté desactivado, tu código aún puede usar $ y no romperse si lo haces:

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

7
2018-03-11 01:33



  1. Para evitar conflictos con otros métodos / bibliotecas en la misma ventana,
  2. Evitar alcance global, hacerlo local,
  3. Para hacer que la depuración sea más rápida (alcance local),
  4. JavaScript tiene solo alcance de función, por lo que también ayudará en la compilación de códigos.

3
2017-10-26 18:52



También deberíamos usar 'use strict' en la función scope para asegurarnos de que el código se ejecute en "modo estricto". Código de muestra que se muestra a continuación

(function() {
    'use strict';

    //Your code from here
})();

1
2017-11-24 06:37