invoked immediately funciones expressions closure javascript scope coding-style iife

immediately - ¿Cuál es el propósito de envolver archivos Javascript completos en funciones anónimas como “(function(){…})()”?



immediately invoked function expressions javascript (8)

En breve

Resumen

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

Ayuda a disminuir las posibilidades de:

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

No detecta cuando el documento está listo, no es algún tipo de document.onload window.onload ni window.onload

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

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á "privada" y será accesible SOLAMENTE dentro del alcance de la función (como lo expresa Vivin Paliath). En otras palabras, estas variables no son visibles / accesibles fuera de la función. Ver demostración en vivo .

Javascript tiene función de alcance. "Los parámetros y las variables definidas 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 cualquier lugar 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 podría hacerse 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 ''myMainFunction'', ya que lo único que queremos es ejecutarlo de inmediato".

Si vuelves a lo básico, descubres que:

  • expression : algo que evalúa a un valor. es decir 3+11/x
  • statement : línea (s) de código que hace algo PERO no se evalúa como un valor. es decir, if(){}

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

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

Así 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 "las declaraciones de función no son invocables" .

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

La siguiente pregunta es "¿por qué no puedo mantenerla como una expresión de función con algo distinto a var myMainFunction = ?

La respuesta es "usted puede", y en realidad hay muchas maneras de hacerlo: ¡agregando un + , un ! , a - , o tal vez envolver 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

Así que una vez que se agrega la modificación relevante a lo que fue nuestro "Código alternativo", volvemos al mismo código 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 ámbitos

Algo que uno podría preguntarse es "¿qué sucede cuando NO se define la variable ''correctamente'' dentro de la función, es decir, se hace una tarea simple?"

(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 alcance actual se le asigna un valor, entonces "se realiza una búsqueda de la cadena de alcance hasta que encuentra la variable o alcanza el alcance global (en qué punto la creará)".

Cuando se encuentra en un entorno de navegador (frente a un entorno de servidor como nodejs), el objeto de window define el alcance global. Por lo tanto podemos hacer window.myOtherFunction() .

Mi consejo de "Buenas prácticas" sobre este tema es usar siempre var cuando se define algo : ya sea un número, objeto o función, e incluso cuando se encuentra en el ámbito global. Esto hace que el código sea mucho más simple.

Nota:

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

Lea más sobre los Javascript Scopes :

Recursos

Próximos pasos

Una vez que obtiene este concepto de IIFE , conduce al module pattern del module pattern , que comúnmente se realiza aprovechando este patrón de IIFE. Que te diviertas :)

Últimamente he estado leyendo mucho Javascript y he notado que todo el archivo está envuelto como el siguiente en los archivos .js que se importarán.

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

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


  1. Para evitar choques con otros métodos / bibliotecas en la misma ventana,
  2. Evita el alcance global, hazlo local,
  3. Para agilizar la depuración (alcance local),
  4. JavaScript tiene solo un alcance de función, por lo que también ayudará en la compilación de códigos.

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

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


Eso se llama un 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 que creé para usar en mi biblioteca.


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

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


Por lo general, se trata del 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 complementos de jQuery se escriben así.

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

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

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

var globalVariable; function someFunction() { var localVariable; }

Correspondientemente

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 a globalFunction() desde cualquier lugar, pero no puede llamar a localFunction1 o localFunction2 .

Lo que está haciendo cuando escribe (function() { ... code ... })() , es que está creando el código dentro de una función literal (lo que significa que todo el "objeto" es en realidad una función). Después de eso, estás invocando la función (la final () ). Así que la principal ventaja de esto, como mencioné anteriormente, es que puede tener métodos / funciones y propiedades privadas:

(function() { var private_var; function private_function() { //code } })()

En el primer ejemplo, globalFunction () fue la función pública a la que se podría llamar para acceder a la funcionalidad pública, pero en el ejemplo anterior, ¿cómo se llama? Aquí, la función de auto-invocación hace que el código se ejecute automáticamente en el 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 como es una función sin nombre, no se puede llamar varias veces, como podría ser initMyStuff ().

Lo bueno es que también puedes definir cosas en el interior y exponerlo 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 puede llamar a myPlugin.public_function1() , ¡pero no puede acceder a private_function() ! Muy similar a una definición de clase. Para entender esto mejor, recomiendo los siguientes enlaces para leer más:

EDITAR

Me olvidé de mencionar. En esa final () , puedes pasar todo lo que quieras dentro. Por ejemplo, cuando creas complementos de jQuery, pasas en jQuery o $ como:

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

Entonces, lo que está haciendo aquí es definir una función que toma un parámetro (llamado jQ , una variable local, y que solo esa función conoce). Entonces, usted auto-invoca la función y pasa un parámetro (también llamado jQuery , pero este es del mundo exterior y es una referencia a la jQuery en sí). No hay 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 en el ámbito global.
  • Hay beneficios para la compresión (minificación).

Anteriormente describí cómo estas funciones se ejecutan automáticamente en el inicio, pero si se ejecutan automáticamente, ¿quién pasa los argumentos? Esta técnica asume 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 manera ya que nuestro ejemplo es una función anónima. Como puede imaginar, una cosa 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.


También deberíamos usar ''usar estricto'' en la función de alcance 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 })();


También puede utilizar los cierres de funciones como datos en expresiones más grandes, como en este método para 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; })() }