Explicar la sintaxis de la función anónima encapsulada


Resumen

¿Puede explicar el razonamiento detrás de la sintaxis para funciones anónimas encapsuladas en JavaScript? ¿Por qué funciona esto: (function(){})(); pero esto no: function(){}();?


Lo que sé

En JavaScript, se crea una función con nombre como esta:

function twoPlusTwo(){
    alert(2 + 2);
}
twoPlusTwo();

También puede crear una función anónima y asignarla a una variable:

var twoPlusTwo = function(){
    alert(2 + 2);
};
twoPlusTwo();

Puede encapsular un bloque de código creando una función anónima, luego envolviéndola entre corchetes y ejecutándola inmediatamente:

(function(){
    alert(2 + 2);
})();

Esto es útil al crear scripts modularizados, para evitar saturar el ámbito actual, o el ámbito global, con variables potencialmente conflictivas , como en el caso de los scripts de Greasemonkey, los complementos de jQuery, etc.

Ahora, entiendo por qué esto funciona. Los corchetes encierran el contenido y exponen solo el resultado (estoy seguro de que hay una mejor manera de describirlo), como con (2 + 2) === 4.


Lo que no entiendo

Pero yo no entiende por qué esto no funciona igual de bien:

function(){
    alert(2 + 2);
}();

¿Puedes explicarme eso?

Author: GG., 2009-10-28

9 answers

No funciona porque se está analizando como un FunctionDeclaration, y el identificador de nombre de las declaraciones de función es obligatorio .

Cuando lo rodeas con paréntesis se evalúa como un FunctionExpression, y las expresiones de función se pueden nombrar o no.

La gramática de un FunctionDeclaration se ve así:

function Identifier ( FormalParameterListopt ) { FunctionBody }

Y FunctionExpression s:

function Identifieropt ( FormalParameterListopt ) { FunctionBody }

Como puede ver el Identifier (Identificadoropt) token en FunctionExpression es opcional, por lo tanto, podemos tener una expresión de función sin un nombre definido:

(function () {
    alert(2 + 2);
}());

O llamada expresión de función:

(function foo() {
    alert(2 + 2);
}());

Los paréntesis (formalmente llamados el Operador de Agrupación) pueden rodear solo expresiones, y se evalúa una expresión de función.

Las dos producciones gramaticales pueden ser ambiguas, y pueden verse exactamente igual, por ejemplo: {[30]]}

function foo () {} // FunctionDeclaration

0,function foo () {} // FunctionExpression

El analizador sabe si es un FunctionDeclaration o un FunctionExpression, dependiendo del contexto donde aparecer.

En el ejemplo anterior, el segundo es una expresión porque el operador de coma también puede manejar solo expresiones.

Por otro lado, FunctionDeclarations en realidad podría aparecer solo en lo que se llama "Program" código, que significa código fuera en el ámbito global, y dentro de la FunctionBody de otras funciones.

Las funciones dentro de los bloques deben evitarse, ya que pueden conducir a un comportamiento impredecible, por ejemplo:

if (true) {
    function foo () { alert('true'); }
} else {
    function foo () { alert('false!'); }
}

foo(); // true? false? why?

El código anterior debería produce un SyntaxError, ya que un Block solo puede contener sentencias (y la especificación ECMAScript no define ninguna sentencia de función), pero la mayoría de las implementaciones son tolerantes, y simplemente tomarán la segunda función, la que alerta 'false!'.

Las implementaciones de Mozilla-Rhino, SpiderMonkey,- tienen un comportamiento diferente. Su gramática contiene una Declaración de función no estándar, lo que significa que la función se evaluará en tiempo de ejecución, no en analizar el tiempo, como sucede con FunctionDeclaration s. En esas implementaciones obtendremos la primera función definida.


Las funciones se pueden declarar de diferentes maneras, compare lo siguiente :

1-Una función definida con el constructor Function asignado a la variable multiply :

var multiply = new Function("x", "y", "return x * y;");

2-Una declaración de función de una función llamada multiply :

function multiply(x, y) {
    return x * y;
}

3-Una expresión de función asignada a la variable multiplicar :

var multiply = function (x, y) {
    return x * y;
};

4-Una expresión de función con nombre func_name , asignada a la variable multiply :

var multiply = function func_name(x, y) {
    return x * y;
};
 367
Author: CMS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-28 00:44:36

A pesar de que esta es una vieja pregunta y respuesta, se trata de un tema que hasta el día de hoy lanza muchos desarrolladores para un bucle. No puedo contar el número de candidatos a desarrolladores de JavaScript que he entrevistado que no pudieron decirme la diferencia entre una declaración de función y una expresión de función y que no tenían idea de lo que es una expresión de función invocada inmediatamente.

Me gustaría mencionar, sin embargo, una cosa muy importante que es que el fragmento de código de Premasagar no funcionaría incluso si le hubiera dado un identificador de nombre.

function someName() {
    alert(2 + 2);
}();

La razón por la que esto no funcionaría es que el motor JavaScript interpreta esto como una declaración de función seguida de un operador de agrupación completamente no relacionado que no contiene ninguna expresión, y los operadores de agrupación deben contener una expresión. Según JavaScript, el fragmento de código anterior es equivalente al siguiente.

function someName() {
    alert(2 + 2);
}

();

Otra cosa que me gustaría señalar que puede ser de alguna utilidad para algunas personas es que cualquier el identificador de nombre que proporcione para una expresión de función es prácticamente inútil en el contexto del código, excepto dentro de la definición de la función en sí.

var a = function b() {
    // do something
};
a(); // works
b(); // doesn't work

var c = function d() {
    window.setTimeout(d, 1000); // works
};

Por supuesto, usar identificadores de nombre con las definiciones de función siempre es útil cuando se trata de depurar código, pero eso es algo completamente diferente... :-)

 42
Author: natlee75,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-26 04:02:50

Ya se han publicado grandes respuestas. Pero quiero señalar que las declaraciones de función devuelven un registro de finalización vacío:

14.1.20 - Semántica en Tiempo de Ejecución: Evaluación

FunctionDeclaration : function BindingIdentifier ( FormalParameters ) { FunctionBody }

  1. Return NormalCompletion(vacío).

Este hecho no es fácil de observe, porque la mayoría de las formas de intentar obtener el valor devuelto convertirán la declaración de función en una expresión de función. Sin embargo, eval lo muestra:

var r = eval("function f(){}");
console.log(r); // undefined

Llamar a un registro de finalización vacío no tiene sentido. Por eso function f(){}() no puede funcionar. De hecho, el motor JS ni siquiera intenta llamarlo, los paréntesis se consideran parte de otra declaración.

Pero si encierra la función entre paréntesis, se convierte en una función expresión:

var r = eval("(function f(){})");
console.log(r); // function f(){}

Las expresiones de función devuelven un objeto de función. Y por lo tanto se puede llamar: (function f(){})().

 12
Author: Oriol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-09 19:01:07

En javascript, esto se llama Expresión de Función inmediatamente Invocada (IIFE) .

Para convertirla en una expresión de función tienes que:

  1. Enciérralo usando ()

  2. Coloque un operador vacío ante él

  3. Asígnalo a una variable.

De lo contrario se tratará como definición de función y entonces no podrá llamarla / invocarla al mismo tiempo de la siguiente manera camino:

 function (arg1) { console.log(arg1) }(); 

Lo anterior te dará error. Porque solo puede invocar una expresión de función inmediatamente.

Esto se puede lograr de dos maneras: Camino 1:

(function(arg1, arg2){
//some code
})(var1, var2);

Camino 2:

(function(arg1, arg2){
//some code
}(var1, var2));

Camino 3:

void function(arg1, arg2){
//some code
}(var1, var2);

Camino 4:

  var ll = function (arg1, arg2) {
      console.log(arg1, arg2);
  }(var1, var2);

Todo lo anterior invocará inmediatamente la expresión de la función.

 6
Author: asmmahmud,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-11 01:27:47

Tengo otra pequeña observación. Su código funcionará con un pequeño cambio:

var x = function(){
    alert(2 + 2);
}();

Utilizo la sintaxis anterior en lugar de la versión más extendida:

var module = (function(){
    alert(2 + 2);
})();

Porque no logré que la sangría funcionara correctamente para los archivos javascript en vim. Parece que a Vim no le gustan los tirantes rizados dentro de paréntesis abiertos.

 3
Author: Andrei Bozantan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-02 00:53:03

Tal vez la respuesta más corta sería que

function() { alert( 2 + 2 ); }

Es una función literalque define una función (anónima). Un par adicional (), que se interpreta como una expresión, no se espera en el nivel superior, solo literales.

(function() { alert( 2 + 2 ); })();

Está en una sentencia de expresión que invoca una función anónima.

 0
Author: theking2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-20 14:00:59
(function(){
     alert(2 + 2);
 })();

Arriba es una sintaxis válida porque cualquier cosa pasada dentro de paréntesis es considerada como expresión de función.

function(){
    alert(2 + 2);
}();

Arriba no es una sintaxis válida. Debido a que el analizador sintáctico de Java script busca el nombre de la función después de la palabra clave de la función, ya que no encuentra nada, arroja un error.

 0
Author: Vithy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-24 19:40:25

Se pueden usar con parámetros-argumentos como

var x = 3; 
var y = 4;

(function(a,b){alert(a + b)})(x,y)

Resultaría como 7

 0
Author: Jude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-07 14:06:57

Esos paréntesis adicionales crean funciones anónimas adicionales entre el espacio de nombres global y la función anónima que contiene el código. Y en Javascript las funciones declaradas dentro de otras funciones solo pueden acceder al espacio de nombres de la función principal que las contiene. Como hay un objeto adicional (función anónima) entre el ámbito global y el ámbito de código real no se conserva.

 0
Author: Jarkko Hietala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-24 08:39:56