¿Cómo funciona el constructo (function () {}) () y por qué lo usa la gente?

79

(function() {})()y su primo específico de jQuery (function($) {})(jQuery)aparecen todo el tiempo en código Javascript.

¿Cómo funcionan estos constructos y qué problemas resuelven?

Ejemplos apreciados

Tom Lehman
fuente
2
No es un duplicado exacto. Las respuestas de las otras preguntas no dicen realmente por qué se ofrece jQuerycomo argumento.
Georg Schölly
1
Siempre me he preguntado por qué se necesitan paréntesis adicionales. Creo que tiene que ver con una ambigüedad entre bloques y objetos literales con llaves. Tenga en cuenta que no son obligatorios si está asignando algo, como var x = function () {} () funciona bien.
jpsimons
1
¿Por qué este 'Cerrado como una pregunta no real'? La pregunta es clara y relevante.
Chris

Respuestas:

73

Con la creciente popularidad de los marcos de JavaScript, el $signo se utilizó en muchas ocasiones diferentes. Entonces, para aliviar posibles choques, puede usar esas construcciones:

(function ($){
  // Your code using $ here.
})(jQuery);

Específicamente, esa es una declaración de función anónima que se ejecuta inmediatamente pasando el objeto jQuery principal como parámetro. Dentro de esa función, puede usar $para referirse a ese objeto, sin preocuparse de que otros marcos también estén dentro del alcance.

Seb
fuente
4
Respuesta breve y significativa. +1!
pestaa
5
Es técnicamente una expresión de función (declaración!). IIFE
John Strickler
¿Puede explicar lo que quiere decir con: "... sin preocuparse de que otros marcos también estén dentro del alcance"?
Redtopia
@Redtopia. El alcance de Javascript se define a nivel de función. Todo lo que está fuera de una función tiene un alcance global. Piense en cuántos Javascript se incluyen en una página web. Cada variable fuera del alcance de una función está en peligro de colisión.
Alberto De Caro
Gracias por aclarar ... no me quedó claro que se refería al alcance local de la función anónima, que no se puede ver desde el alcance global, o el alcance de ninguna otra función.
Redtopia
41

Ésta es una técnica que se utiliza para limitar el alcance de las variables; es la única forma de evitar que las variables contaminen el espacio de nombres global.

var bar = 1; // bar is now part of the global namespace
alert(bar);

(function () {
   var foo = 1; // foo has function scope
   alert(foo); 
   // code to be executed goes here
})();
Daniel LeCheminant
fuente
7
... y aún así tenerlos "globales" con respecto a su código.
tvanfosson
Entonces, ¿la referencia a Opera en el código de ejemplo es irrelevante / incorrecta?
Bobby Jack
1
@Bobby: puede haber variables / funciones adicionales dentro de ese bloque que se usan solo para Opera.
geowa4
19

1) Define una función anónima y la ejecuta de inmediato.

2) Por lo general, se hace para no contaminar el espacio de nombres global con código no deseado.

3) Necesita exponer algunos métodos de él, cualquier cosa declarada dentro será "privada", por ejemplo:

MyLib = (function(){
    // other private stuff here
    return {
        init: function(){
        }
    };

})();

O alternativamente:

MyLib = {};
(function({
    MyLib.foo = function(){
    }
}));

El punto es que hay muchas formas de usarlo, pero el resultado sigue siendo el mismo.

Evan Trimboli
fuente
1. Tal función se define en un archivo separado, ¿cómo se ejecuta? (parece que nadie llama a esta función, entonces, ¿cómo se desencadena la función?) 2. No vi que la función en el nivel superior tenga una declaración "return" ... Estoy leyendo dojo.util._doh ._browserRunner.js ¡Gracias!
Paul
1) Como dije, se ejecuta solo, el último () le dice que se ejecute. 2) No necesariamente tiene que tener un retorno, también puede establecer valores en una propiedad en particular. Fue simplemente un ejemplo. Actualicé la respuesta con otra posibilidad.
Evan Trimboli
9

Es solo una función anónima que se llama inmediatamente. Primero puede crear la función y luego llamarla, y obtiene el mismo efecto:

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

trabaja como:

temp = function(){ ... };
temp();

También puede hacer lo mismo con una función con nombre:

function temp() { ... }
temp();

El código que llamas específico de jQuery es solo eso en el sentido de que usas el objeto jQuery en él. Es solo una función anónima con un parámetro, que se llama inmediatamente.

Puede hacer lo mismo en dos pasos, y puede hacerlo con los parámetros que desee:

temp = function(answer){ ... };
temp(42);

El problema que esto resuelve es que crea un uso cerrado para el código en la función. Puede declarar variables en él sin contaminar el espacio de nombres global, reduciendo así el riesgo de conflictos al usar un script junto con otro.

En el caso específico de jQuery, lo usa en modo de compatibilidad donde no declara el nombre $ como un alias para jQuery. Al enviar el objeto jQuery al cierre y nombrar el parámetro $, aún puede usar la misma sintaxis que sin el modo de compatibilidad.

Guffa
fuente
Como comenté en la respuesta de @ spoulson, una función anónima no es lo mismo que un cierre.
Tim Down
7

Aquí explica que su primera construcción proporciona un ámbito para las variables.

Las variables tienen un alcance a nivel de función en javascript. Esto es diferente a lo que podría estar acostumbrado en un lenguaje como C # o Java, donde las variables tienen como alcance el bloque. Lo que esto significa es que si declara una variable dentro de un bucle o una instrucción if, estará disponible para toda la función.

Si alguna vez necesita establecer el alcance de una variable dentro de una función, puede usar una función anónima para hacerlo. En realidad, puede crear una función anónima y luego ejecutarla de inmediato y todas las variables dentro estarán en el ámbito de la función anónima:

(function() {
  var myProperty = "hello world";
  alert(myProperty);
})();
alert(typeof(myProperty)); // undefined
Ewan Todd
fuente
6

Otra razón para hacer esto es eliminar cualquier confusión sobre qué $operador de marco está utilizando. Para forzar jQuery, por ejemplo, puede hacer:

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

Al pasar el $operador como parámetro e invocarlo en jQuery, el $operador dentro de la función se bloquea en jQuery incluso si tiene otros marcos cargados.

tvanfosson
fuente
Esa es una variación más específica de la pregunta original, ¡pero sigue siendo buena información! :)
Bobby Jack
No es una respuesta en absoluto, ¡pero la he mejorado por la magnífica idea que tuvo!
Spidey
5

Otro uso de esta construcción es "capturar" los valores de las variables locales que se utilizarán en un cierre. Por ejemplo:

for (var i = 0; i < 3; i++) {
    $("#button"+i).click(function() {
        alert(i);
    });
}

El código anterior hará que los tres botones aparezcan como "3". Por otra parte:

for (var i = 0; i < 3; i++) {
    (function(i) {
        $("#button"+i).click(function() {
            alert(i);
        });
    })(i);
}

Esto hará que los tres botones aparezcan "0", "1" y "2" como se esperaba.

La razón de esto es que un cierre mantiene una referencia a su marco de pila adjunto , que contiene los valores actuales de sus variables. Si esas variables cambian antes de que se ejecute el cierre, el cierre verá solo los últimos valores, no los valores como estaban en el momento en que se creó el cierre. Al envolver la creación del cierre dentro de otra función como en el segundo ejemplo anterior, el valor actual de la variable ise guarda en el marco de pila de la función anónima.

Greg Hewgill
fuente
Un pequeño detalle, pero no es el marco de pila lo que está cerrado, son los ámbitos léxicos circundantes. Las buenas implementaciones de cierres solo capturarán los símbolos que realmente se mencionan dentro del cierre, algunas implementaciones simplemente capturan todo (Ruby hace esto, así que tenga cuidado con lo que tiene cerca de cierres). Una forma de pensar en un objeto en OOP es como un cierre donde los símbolos capturados se definen explícitamente (los atributos de los miembros del objeto).
KayEss
Eso es cierto, pero en realidad es solo un problema de implementación y, conceptualmente, se puede considerar que el marco de pila es lo que hace referencia al cierre. Los "objetos" de estilo POO en los lenguajes de la familia Lisp se hacen con cierres, es una técnica poderosa.
Greg Hewgill
Sí, aunque si quiere pensar en términos de marcos de pila, entonces el cierre es una cadena de marcos de pila, un marco para cada función envolvente en el ámbito léxico junto con el ámbito global. Es más fácil pensar en el alcance léxico (especialmente cuando se le explica a alguien que es nuevo en los cierres) porque no entra en una discusión inútil (y engañosa) sobre qué marco de pila cuando tiene varios niveles de funciones anidadas. El alcance léxico es simplemente lo que ves en el código fuente.
KayEss
4

Esto se considera un cierre . Significa que el código contenido se ejecutará dentro de su propio ámbito léxico. Esto significa que puede definir nuevas variables y funciones y no colisionarán con el espacio de nombres utilizado en el código fuera del cierre.

var i = 0;
alert("The magic number is " + i);

(function() {
   var i = 99;
   alert("The magic number inside the closure is " + i);
})();

alert("The magic number is still " + i);

Esto generará tres ventanas emergentes, demostrando que ien el cierre no altera la variable preexistente del mismo nombre:

  • El número mágico es 0
  • El número mágico dentro del cierre es 99
  • El número mágico sigue siendo 0
Spoulson
fuente
5
Eso no es un cierre, es simplemente demostrar que una función tiene su propio alcance. Un cierre se forma cuando una función se define dentro de otra función y se vuelve disponible fuera de esa función , conservando así su acceso a las variables, funciones y parámetros en la función externa incluso después de que esa función externa haya regresado.
Tim Down
Simplemente cambie "(function () {..." a "var func = function () {...}; func ();". Ahí, cierre.
spoulson
No quiero trabajar con esto, pero ... todavía no hay cierre: no se está haciendo ninguna función interna fuera de la función externa. Una manera fácil de crear un cierre en su ejemplo sería devolver una función que alertara i: var f = (function () {var i = 99; return function () {alert (i);};}) (); F();
Tim Down
Punto anotado. Todo se reduce a los detalles de la implementación.
spoulson
2

A menudo se usan en complementos de jQuery. Como se explica en la Guía de creación de complementos de jQuery, todas las variables declaradas en el interior { }son privadas y no son visibles desde el exterior, lo que permite una mejor encapsulación.

Darin Dimitrov
fuente
3
{}no crean por sí mismos un nuevo alcance en JavaScript. El alcance se crea mediante la declaración de función.
Annabelle
2

Como han dicho otros, ambos definen funciones anónimas que se invocan de inmediato. Por lo general, envuelvo mis declaraciones de clase de JavaScript en esta estructura para crear un ámbito privado estático para la clase. Luego puedo colocar datos constantes, métodos estáticos, controladores de eventos o cualquier otra cosa en ese alcance y solo será visible para las instancias de la clase:

// Declare a namespace object.
window.MyLibrary = {};

// Wrap class declaration to create a private static scope.
(function() {
  var incrementingID = 0;

  function somePrivateStaticMethod() {
    // ...
  }

  // Declare the MyObject class under the MyLibrary namespace.
  MyLibrary.MyObject = function() {
    this.id = incrementingID++;
  };

  // ...MyObject's prototype declaration goes here, etc...
  MyLibrary.MyObject.prototype = {
    memberMethod: function() {
      // Do some stuff
      // Maybe call a static private method!
      somePrivateStaticMethod();
    }
  };
})();

En este ejemplo, la MyObjectclase se asigna al MyLibraryespacio de nombres, por lo que es accesible. incrementingIDy somePrivateStaticMethod()no son directamente accesibles fuera del alcance de la función anónima.

Annabelle
fuente
2

Eso es básicamente el espacio de nombres de su código JavaScript.

Por ejemplo, puede colocar cualquier variable o función allí, y desde el exterior, no existen en ese ámbito. Entonces, cuando encapsula todo allí, no tiene que preocuparse por los enfrentamientos.

El ()al final significa autoinvocarse. También puede agregar un argumento allí que se convertirá en el argumento de su función anónima. Hago esto con jQuery a menudo, y puedes ver por qué ...

(function($) {

    // Now I can use $, but it won't affect any other library like Prototype
})(jQuery);

Evan Trimboli cubre el resto en su respuesta .

alex
fuente
1

Es una función que se invoca a sí misma. Una especie de taquigrafía para escribir

function DoSomeStuff($)
{
}

DoSomeStuff(jQuery);
Mateo
fuente
1

Lo que hace el código anterior es crear una función anónima en la línea 1 y luego llamarla en la línea 3 con 0 argumentos. Esto encapsula efectivamente todas las funciones y variables definidas dentro de esa biblioteca, porque todas las funciones serán accesibles solo dentro de esa función anónima.

Esta es una buena práctica, y el razonamiento detrás de ella es evitar contaminar el espacio de nombres global con variables y funciones, que podrían ser golpeadas por otras partes de Javascript en todo el sitio.

Para aclarar cómo se llama a la función, considere el ejemplo simple:

Si tiene esta única línea de Javascript incluida, se invocará automáticamente sin llamarla explícitamente:

alert('hello');

Entonces, toma esa idea y aplícala a este ejemplo:

(function() {
    alert('hello')
    //anything I define in here is scoped to this function only
}) (); //here, the anonymous function is invoked

El resultado final es similar, porque la función anónima se invoca como en el ejemplo anterior.

wsanville
fuente
por favor vea mi primera pregunta de los comentarios a Evan ... ¡Gracias!
Paul
//here, the anonymous function is invoked, simple, pero no lo vi antes :)
Stefan
1

Porque las buenas respuestas del código ya están tomadas :) Lanzaré una sugerencia para ver algunos videos de John Resig video 1 , video 2 (inventor de jQuery y master en JavaScript).

Algunas ideas y respuestas realmente buenas proporcionadas en los videos.

Eso es lo que estaba haciendo en el momento en que vi su pregunta.

John K
fuente
0
function(){ // some code here }

es la forma de definir una función anónima en javascript. Pueden brindarle la capacidad de ejecutar una función en el contexto de otra función (donde es posible que no tenga esa capacidad de otra manera).

Justin Niessner
fuente