¿Por qué necesita invocar una función anónima en la misma línea?

374

Estaba leyendo algunas publicaciones sobre cierres y vi esto en todas partes, pero no hay una explicación clara de cómo funciona, cada vez que me dijeron que lo usara ...:

// Create a new anonymous function, to use as a wrapper
(function(){
    // The variable that would, normally, be global
    var msg = "Thanks for visiting!";

    // Binding a new function to a global object
    window.onunload = function(){
        // Which uses the 'hidden' variable
        alert( msg );
    };
// Close off the anonymous function and execute it
})();

Ok, veo que crearemos una nueva función anónima y luego la ejecutaremos. Entonces, después de eso, este código simple debería funcionar (y lo hace):

(function (msg){alert(msg)})('SO');

Mi pregunta es ¿qué tipo de magia sucede aquí? Pensé que cuando escribí:

(function (msg){alert(msg)})

entonces se crearía una nueva función sin nombre como la función "" (msg) ...

pero entonces ¿por qué esto no funciona?

(function (msg){alert(msg)});
('SO');

¿Por qué necesita estar en la misma línea?

¿Podrías señalarme algunas publicaciones o darme una explicación?

palig
fuente
2
En otros idiomas, estos se denominan punteros de función o delegados, si desea examinar las estructuras de nivel inferior involucradas.
Chris Moschini
17
Usted tiene un ; en la primera línea
Oliver Ni
Ahora que sabes cómo funciona ... No lo uses. Deberíamos dejar de escribir funciones anónimas . ¡Con solo unos pocos caracteres más podemos darle un nombre real a nuestras funciones y hacer que la depuración del código Javascript sea mucho más fácil!
Stijn de Witt
1
La línea (function (msg){alert(msg)})('SO');funciona completamente por sí sola. No tiene nada que ver con la otra función anónima que publicó antes. Esas son dos funciones anónimas completamente separadas. Debe invocar una función anónima de inmediato porque no tiene nombre y no se puede hacer referencia después.
Octopus

Respuestas:

380

Suelte el punto y coma después de la definición de la función.

(function (msg){alert(msg)})
('SO');

Arriba debería funcionar.

Página DEMO: https://jsfiddle.net/e7ooeq6m/

He discutido este tipo de patrón en esta publicación:

jQuery y $ preguntas

EDITAR:

Si observa la especificación del script ECMA , hay 3 formas en que puede definir una función. (Página 98, Sección 13 Definición de funciones)

1. Usando el constructor de funciones

var sum = new Function('a','b', 'return a + b;');
alert(sum(10, 20)); //alerts 30

2. Uso de la declaración de función.

function sum(a, b)
{
    return a + b;
}

alert(sum(10, 10)); //Alerts 20;

3. Expresión de funciones

var sum = function(a, b) { return a + b; }

alert(sum(5, 5)); // alerts 10

Entonces puede preguntar, ¿cuál es la diferencia entre declaración y expresión?

De la especificación del script ECMA:

FunctionDeclaration: identificador de función (FormalParameterListopt) {FunctionBody}

FunctionExpression: function Identifieropt (FormalParameterListopt) {FunctionBody}

Si observa, 'identificador' es opcional para la expresión de la función. Y cuando no proporciona un identificador, crea una función anónima. No significa que no pueda especificar un identificador.

Esto significa que lo siguiente es válido.

var sum = function mySum(a, b) { return a + b; }

Un punto importante a tener en cuenta es que puede usar 'mySum' solo dentro del cuerpo de la función mySum, no afuera. Ver el siguiente ejemplo:

var test1 = function test2() { alert(typeof test2); }

alert(typeof(test2)); //alerts 'undefined', surprise! 

test1(); //alerts 'function' because test2 is a function.

Demo en vivo

Compara esto con

 function test1() { alert(typeof test1) };

 alert(typeof test1); //alerts 'function'

 test1(); //alerts 'function'

Armados con este conocimiento, intentemos analizar su código.

Cuando tienes un código como,

    function(msg) { alert(msg); }

Creaste una expresión de función. Y puede ejecutar esta expresión de función envolviéndola entre paréntesis.

    (function(msg) { alert(msg); })('SO'); //alerts SO.
SolutionYogi
fuente
1
Si, pero porque? ¿Por qué debe ser como en línea? No importa cuántos espacios en blanco usaré.
palig
99
Como escribí, el punto y coma terminó la definición de función anónima. Como no tiene nombre (¡es anónimo duh!), Ya no podrá llamarlo. Si no pone punto y coma, la función aún podría ejecutarse.
SolutionYogi el
Pensé que la inserción automática de punto y coma pondría un punto y coma en este caso, pero no es así. Entonces tienes razón.
Nosredna
1
Nosredna, JS se comporta poco arbitrariamente cuando se trata de agregar punto y coma. Lea este artículo detallado: blog.boyet.com/blog/javascriptlessons/…
SolutionYogi el
Sí, veo eso (function (msg) {alert (msg)}) ('SO'); trabajos. Solo preguntaba por qué funciona. Donde se especifica esto o qué tipo de característica JS es esta. Entonces, una vez que llamo: (function (msg) {alert (msg)}) ¿qué pasará con la función? Será GC'ed?
palig
129

Se llama una función auto invocada.

Lo que está haciendo cuando llama (function(){})es devolver un objeto de función. Cuando lo anexas (), se invoca y se ejecuta cualquier cosa en el cuerpo. El ;denota el final de la instrucción, es por eso que no pasa la segunda invocación.

seth
fuente
Ah ok ya veo, así que es solo una sintaxis especial de JS, ¿verdad? ¡Me gusta más esta explicación! Simple and short :)
palig
Creo que es incorrecto decir que el cuerpo será 'evadido'. Se ejecuta como cualquier otra función. Debido a que es anónimo, puede guardar la referencia en algún lugar O ejecutarla de inmediato.
SoluciónYogi
16
Personalmente, ni siquiera me gusta el término 'función de auto invocación'. No es que la función se invoque a sí misma. El programador escribió esos paréntesis para invocarlo.
SolutionYogi el
No es "sintaxis especial" más que cualquier otra cosa es especial. En realidad, el formulario "nombre de función (args) {BLOQUE}" es mucho más "especial". En realidad es azúcar innecesario; esto, sin embargo, es lo que realmente hace que las cosas sucedan.
jrockway
2
Buen enlace al artículo. Señala por qué alguien usaría esto citado: "En un esfuerzo por proteger el objeto global, todas las aplicaciones de JavaScript deben escribirse dentro de una función de invocación automática. Esto creará un ámbito de aplicación en el que se pueden crear variables sin temor a que colisionen con otras aplicaciones ". Y también señaló "Una vez que la función termina, las variables se descartan y el objeto global permanece sin cambios".
yeahdixon
94

Una cosa que encontré confusa es que los "()" son operadores de agrupación.

Aquí está su función básica declarada.

Ex. 1:

var message = 'SO';

function foo(msg) {
    alert(msg);
}

foo(message);

Las funciones son objetos y se pueden agrupar. Así que echemos parens alrededor de la función.

Ex. 2:

var message = 'SO';

function foo(msg) {  //declares foo
    alert(msg);
}

(foo)(message);     // calls foo

Ahora, en lugar de declarar y llamar de inmediato a la misma función, podemos usar la sustitución básica para declararla como la llamamos.

Ex. 3)

var message = 'SO';

(function foo(msg) {
    alert(msg);
})(message);          // declares & calls foo

Finalmente, no tenemos necesidad de ese foo extra porque no estamos usando el nombre para llamarlo. Las funciones pueden ser anónimas.

Ex. 4)

var message = 'SO';

(function (msg) {   // remove unnecessary reference to foo
    alert(msg);
})(message);

Para responder a su pregunta, consulte el Ejemplo 2. Su primera línea declara alguna función sin nombre y la agrupa, pero no la llama. La segunda línea agrupa una cadena. Ambos no hacen nada. (El primer ejemplo de Vincent).

(function (msg){alert(msg)});  
('SO');                       // nothing.

(foo); 
(msg); //Still nothing.

Pero

(foo)
(msg); //works
Benxamin
fuente
66
Gracias. Sus ejemplos fueron bastante claros. No sabía que los paréntesis en JavaScript podrían cambiar el significado del código de esta manera. Vengo de un fondo Java, así que aprendo algo nuevo (y a menudo inesperado) sobre JavaScript casi todos los días que lo uso.
hotshot309
55
Gracias por hacerlo paso a paso, esto es mucho mejor que cualquier otra explicación que haya visto. +1
Wk_of_Angmar
2
Momento importante de la AHA aquí, y gracias por ilustrar con sustitución. +100
FredTheWebGuy
1
Una de las mejores explicaciones que he leído sobre funciones anónimas. ¡Muchas gracias!
Teknotica
23

Una función anónima no es una función con el nombre "". Es simplemente una función sin nombre.

Al igual que cualquier otro valor en JavaScript, una función no necesita un nombre para crearse. Aunque es mucho más útil vincularlo a un nombre como cualquier otro valor.

Pero como cualquier otro valor, a veces desea usarlo sin vincularlo a un nombre. Ese es el patrón de auto-invocación.

Aquí hay una función y un número, no vinculados, no hacen nada y nunca se pueden usar:

function(){ alert("plop"); }
2;

Entonces tenemos que almacenarlos en una variable para poder usarlos, como cualquier otro valor:

var f = function(){ alert("plop"); }
var n = 2;

También puede usar azúcar sintáctico para unir la función a una variable:

function f(){ alert("plop"); }
var n = 2;

Pero si no es necesario nombrarlos y generaría más confusión y menos legibilidad, puede usarlos de inmediato.

(function(){ alert("plop"); })(); // will display "plop"
alert(2 + 3); // will display 5

Aquí, mi función y mis números no están vinculados a una variable, pero aún pueden usarse.

Dicho así, parece que la función de invocación automática no tiene ningún valor real. Pero debe tener en cuenta que el delimitador de alcance de JavaScript es la función y no el bloque ({}).

Por lo tanto, una función de invocación automática tiene el mismo significado que un bloque C ++, C # o Java. Lo que significa que la variable creada dentro no se "escapará" fuera del alcance. Esto es muy útil en JavaScript para no contaminar el alcance global.

Vincent Robert
fuente
Buen post. ¿Qué pasará con la función '() {alert ("plop"); } 'cuando lo ejecuté? Será GC'ed?
palig
2
La función () {alert ("plop"); } la instrucción simplemente asigna la función pero no la ejecuta ni la vincula a una variable. Dado que la función creada no está vinculada a ninguna variable, se GCed rápidamente.
Vincent Robert
Este hilo SO va más allá del alcance de lo que estamos hablando aquí, pero explica las formas de separar los espacios de nombres de JavaScript e incluye ejemplos que utilizan funciones de invocación automática.
hotshot309
19

Así es como funciona JavaScript. Puede declarar una función con nombre:

function foo(msg){
   alert(msg);
}

Y llámalo:

foo("Hi!");

O puede declarar una función anónima:

var foo = function (msg) {
    alert(msg);
}

Y llama eso:

foo("Hi!");

O bien, nunca puedes vincular la función a un nombre:

(function(msg){
   alert(msg);
 })("Hi!");

Las funciones también pueden devolver funciones:

function make_foo() {
    return function(msg){ alert(msg) };
}

(make_foo())("Hi!");

No vale nada que cualquier variable definida con "var" en el cuerpo de make_foosea ​​cerrada por cada función devuelta por make_foo. Esto es un cierre, y significa que cualquier cambio realizado en el valor por una función será visible por otra.

Esto le permite encapsular información, si lo desea:

function make_greeter(msg){
    return function() { alert(msg) };
}

var hello = make_greeter("Hello!");

hello();

Es casi lo que funciona cada lenguaje de programación, excepto Java.

jrockway
fuente
8

El código que muestra,

(function (msg){alert(msg)});
('SO');

consisten en dos declaraciones. La primera es una expresión que produce un objeto de función (que luego se recolectará como basura porque no se guarda). La segunda es una expresión que produce una cadena. Para aplicar la función a la cadena, debe pasar la cadena como argumento a la función cuando se crea (que también se muestra arriba), o deberá almacenar la función en una variable, para que pueda aplíquelo en otro momento, a su gusto. Al igual que:

var f = (function (msg){alert(msg)});
f('SO');

Tenga en cuenta que al almacenar una función anónima (una función lambda) en una variable, efectivamente le está dando un nombre. Por lo tanto, puede definir una función regular:

function f(msg) {alert(msg)};
f('SO');
Stephan202
fuente
7

En resumen de los comentarios anteriores:

function() {
  alert("hello");
}();

cuando no se asigna a una variable, produce un error de sintaxis. El código se analiza como una declaración de función (o definición), que hace que los paréntesis de cierre sean sintácticamente incorrectos. Agregar paréntesis alrededor de la parte de la función le dice al intérprete (y al programador) que esta es una expresión de función (o invocación), como en

(function() {
  alert("hello");
})();

Esta es una función de invocación automática, lo que significa que se crea de forma anónima y se ejecuta de inmediato porque la invocación ocurre en la misma línea donde se declara. Esta función de auto-invocación se indica con la sintaxis familiar para llamar a una función sin argumentos, además de paréntesis, agregó alrededor del nombre de la función: (myFunction)();.

Hay una buena sintaxis de la función JavaScript de discusión SO .

hotshot309
fuente
3

Esta respuesta no está estrictamente relacionada con la pregunta, pero es posible que le interese descubrir que este tipo de característica de sintaxis no es específica de las funciones. Por ejemplo, siempre podemos hacer algo como esto:

alert(
    {foo: "I am foo", bar: "I am bar"}.foo
); // alerts "I am foo"

Relacionado con las funciones. Como son objetos, que heredan de Function.prototype, podemos hacer cosas como:

Function.prototype.foo = function () {
    return function () {
        alert("foo");
    };
};

var bar = (function () {}).foo();

bar(); // alerts foo

Y ya sabes, ni siquiera tenemos que rodear las funciones con paréntesis para ejecutarlas. De todos modos, siempre que tratemos de asignar el resultado a una variable.

var x = function () {} (); // this function is executed but does nothing

function () {} (); // syntax error

Otra cosa que puede hacer con las funciones, tan pronto como las declare, es invocar al newoperador sobre ellas y obtener un objeto. Los siguientes son equivalentes:

var obj = new function () {
    this.foo = "bar";
};

var obj = {
    foo : "bar"
};
Ionuț G. Stan
fuente
3

Hay una propiedad más que tiene la función JavaScript. Si desea llamar a la misma función anónima de forma recursiva.

(function forInternalOnly(){

  //you can use forInternalOnly to call this anonymous function
  /// forInternalOnly can be used inside function only, like
  var result = forInternalOnly();
})();

//this will not work
forInternalOnly();// no such a method exist
Anoop
fuente
2
+1 Se agregó una pequeña muestra para que quede más claro :-) La primera vez que lo leí tuve que releerlo 4 veces.
xanatos
3

Mi comprensión de la pregunta del autor de la pregunta es tal que:

¿Cómo funciona esta magia?

(function(){}) ('input')   // Used in his example

Puedo estar equivocado. Sin embargo, la práctica habitual con la que las personas están familiarizadas es:

(function(){}('input') )

La razón es que JavaScript paréntesis AKA (), no puede contener declaraciones y cuando el analizador encuentra la palabra clave de función, sabe analizarla como una expresión de función y no como una declaración de función.

Fuente: publicación de blog Expresión de función invocada inmediatamente (IIFE)

laycat
fuente
3

ejemplos sin corchetes:

void function (msg) { alert(msg); }
('SO');

(este es el único uso real de void, afaik)

o

var a = function (msg) { alert(msg); }
('SO');

o

!function (msg) { alert(msg); }
('SO');

funciona igual de bien la voidestá causando la expresión para evaluar, así como la asignación y la explosión. el último que trabaja con ~, +, -, delete, typeof, algunos de los operadores unitarios ( voides uno también). no funcionan son de hecho ++, --debido al requisito de una variable.

El salto de línea no es necesario.

Nina Scholz
fuente
@Bergi en ie11 deletefunciona. incluso con 'use strict';. esto también funciona:delete (3 + 4);
Nina Scholz
Vaya, mi error. " 2) Si Tipo (ref) no es Referencia, devuelve verdadero " . Solo arroja errores para referencias reales que no se pueden resolver.
Bergi
1

Es una función anónima de ejecución automática. El primer conjunto de corchetes contiene las expresiones que se ejecutarán, y el segundo conjunto de corchetes ejecuta esas expresiones.

(function () {
    return ( 10 + 20 );
})();

Peter Michaux discute la diferencia en un par importante de paréntesis .

Es una construcción útil cuando se intenta ocultar variables del espacio de nombres principal. Todo el código dentro de la función está contenido en el ámbito privado de la función, lo que significa que no se puede acceder a él desde fuera de la función, por lo que es verdaderamente privado.

Ver:

  1. Cierre (informática)
  2. Espaciado de nombres de JavaScript
  3. Par importante de paréntesis Javascript
Ashwin Parmar
fuente
0

Otro punto de vista

Primero, puede declarar una función anónima:

var foo = function(msg){
 alert(msg);
}

Entonces lo llamas:

foo ('Few');

Porque foo = function (msg) {alert (msg);} por lo que puede reemplazar foo como:

function(msg){
 alert(msg);
} ('Few');

Pero debe envolver toda su función anónima dentro de un par de llaves para evitar el error de sintaxis de declarar la función al analizar. Entonces nosotros tenemos,

(function(msg){
 alert(msg);
}) ('Few');

De esta manera, es fácil de entender para mí.

capu
fuente
0

Cuando lo hiciste:

(function (msg){alert(msg)});
('SO');

Terminó la función antes ('SO')debido al punto y coma. Si solo escribes:

(function (msg){alert(msg)})
('SO');

Funcionará.

Ejemplo de trabajo: http://jsfiddle.net/oliverni/dbVjg/

Oliver Ni
fuente
0

La razón simple por la que no funciona no se debe a que ;indica el final de la función anónima. Es porque sin el ()fin de una llamada a la función, no es una llamada a la función. Es decir,

function help() {return true;}

Si llama, result = help();es una llamada a una función y devolverá verdadero.

Si llamas result = help;esto no es una llamada. Es una tarea en la que la ayuda se trata como datos que se asignarán al resultado.

Lo que hiciste fue declarar / instanciar una función anónima agregando el punto y coma,

(function (msg) { /* Code here */ });

y luego traté de llamarlo en otra declaración usando solo paréntesis ... Obviamente porque la función no tiene nombre, pero esto no funcionará:

('SO');

El intérprete ve los paréntesis en la segunda línea como una nueva instrucción / declaración, y por lo tanto no funciona, incluso si lo hizo así:

(function (msg){/*code here*/});('SO');

Todavía no funciona, pero funciona cuando elimina el punto y coma porque el intérprete ignora los espacios en blanco y los carruajes y ve el código completo como una declaración.

(function (msg){/*code here*/})        // This space is ignored by the interpreter
('SO');

Conclusión: una llamada a la función no es una llamada a la función sin el ()fin a menos que bajo condiciones específicas, como ser invocado por otra función, es decir, onload = 'help' ejecutaría la función de ayuda aunque los paréntesis no estuvieran incluidos. Creo que setTimeout y setInterval también permiten este tipo de llamadas a funciones, y también creo que el intérprete agrega los paréntesis detrás de escena de todos modos, lo que nos lleva nuevamente a "una llamada a funciones no es una llamada a funciones sin paréntesis".

técnicas contratadas
fuente
No entiendo por qué esto recibió tantos votos negativos. Creo que es una respuesta aceptable? : /
Daniel Cheung
0
(function (msg){alert(msg)})
('SO');

Este es un método común para usar una función anónima como cierre que utilizan muchos marcos JavaScript.

Esta función llamada es automática cuando se compila el código.

Si coloca ; en la primera línea, el compilador lo trata como dos líneas diferentes. Por lo tanto, no puede obtener los mismos resultados que anteriormente.

Esto también se puede escribir como:

(function (msg){alert(msg)}('SO'));

Para obtener más detalles, consulte JavaScript / Funciones anónimas .

user2349539
fuente
Hasta donde yo sé, JavaScript no se "compila"
Daniel Cheung
0
  1. Las funciones anónimas son funciones que se declaran dinámicamente en tiempo de ejecución. Se llaman funciones anónimas porque no se les da un nombre de la misma manera que las funciones normales.

    Las funciones anónimas se declaran utilizando el operador de función en lugar de la declaración de función. Puede usar el operador de función para crear una nueva función siempre que sea válida para poner una expresión. Por ejemplo, podría declarar una nueva función como parámetro para una llamada de función o asignar una propiedad de otro objeto.

    Aquí hay un ejemplo típico de una función con nombre:

    función flyToTheMoon () {alert ("¡Zoom! ¡Zoom! ¡Zoom!"); } volar a la luna(); Aquí está el mismo ejemplo creado como una función anónima:

    var flyToTheMoon = function () {alert ("¡Zoom! ¡Zoom! ¡Zoom!"); } volar a la luna();

    Para más detalles, lea aquí:

    http://helephant.com/2008/08/23/javascript-anonymous-functions/

Harikesh Yadav
fuente
0

El IIFE simplemente compartimenta la función y oculta la msgvariable para no "contaminar" el espacio de nombres global. En realidad, manténgalo simple y haga lo siguiente a menos que esté creando un sitio web de mil millones de dólares.

var msg = "later dude";
window.onunload = function(msg){
  alert( msg );
};

Puede asignar un espacio de nombre a su msgpropiedad utilizando un patrón de módulo revelador como:

var myScript = (function() {
    var pub = {};
    //myscript.msg
    pub.msg = "later dude";
    window.onunload = function(msg) {
        alert(msg);
    };
    //API
    return pub;
}());
Ronnie Royston
fuente
-1

Las funciones anónimas están destinadas a ser una operación única en la que define una función sobre la marcha para que genere una salida de usted a partir de una entrada que está proporcionando. Excepto que no proporcionó la entrada. En cambio, escribiste algo en la segunda línea ('SO'); - una declaración independiente que no tiene nada que ver con la función. ¿Que esperabas? :)

Vietnhi Phuvan
fuente
No es 100% correcto. Esta es una función anónima, así y está destinado a ser reutilizado: var foo = function() {};. Sin embargo, todo lo demás está bien.
Felix Kling