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

Resuelto Prem asked hace 15 años • 10 respuestas

Resumen

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


Lo que yo sé

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

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

También puedes 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 alcance actual o el alcance 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 no entiendo por qué esto no funciona igual de bien:

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

¿Puedes explicarme eso?

Prem avatar Oct 28 '09 06:10 Prem
Aceptado

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

Cuando lo rodea entre paréntesis, se evalúa como FunctionExpressiony las expresiones de función pueden tener nombre o no.

La gramática de a FunctionDeclarationse ve así:

function Identifier ( FormalParameterListopt ) { FunctionBody }

Y FunctionExpressions:

function Identifieropt ( FormalParameterListopt ) { FunctionBody }

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

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

O expresión de función nombrada :

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

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

Las dos producciones gramaticales pueden ser ambiguas y pueden verse exactamente iguales, por ejemplo:

function foo () {} // FunctionDeclaration

0,function foo () {} // FunctionExpression

El analizador sabe si es a FunctionDeclarationo a FunctionExpression, dependiendo del contexto donde aparece.

En el ejemplo anterior, la segunda es una expresión porque el operador de coma también puede manejar sólo expresiones.

Por otro lado, FunctionDeclarations podría aparecer sólo en lo que se llama Programcódigo " ", es decir, código fuera del alcance global y dentro de FunctionBodyotras funciones.

Se deben evitar las funciones dentro de bloques, porque pueden provocar un comportamiento impredecible, por ejemplo:

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

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

El código anterior en realidad debería producir un SyntaxError, ya que a Blocksolo puede contener declaraciones (y la especificación ECMAScript no define ninguna declaración 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 tiempo de análisis, como sucede con FunctionDeclarations. 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 de funciones asignado a la variable multiplicar :

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

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

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 nombrada func_name , asignada a la variable multiplicar :

var multiply = function func_name(x, y) {
    return x * y;
};
Christian C. Salvadó avatar Oct 27 '2009 23:10 Christian C. Salvadó

Aunque esta es una pregunta y respuesta antiguas, analiza un tema que hasta el día de hoy desconcierta a muchos desarrolladores. No puedo contar la cantidad de candidatos a desarrolladores de JavaScript que entrevisté y que no podían decirme la diferencia entre una declaración de función y una expresión de función y que no tenían idea de qué es una expresión de función invocada inmediatamente.

Sin embargo, me gustaría mencionar una cosa muy importante: 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 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 misma.

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 nombres con las definiciones de funciones siempre es útil cuando se trata de depurar código, pero eso es algo completamente distinto... :-)

natlee75 avatar Aug 26 '2012 04:08 natlee75

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

14.1.20 - Semántica en tiempo de ejecución: Evaluación

Declaración de función : function BindingIdentifier ( FormalParameters ) { FunctionBody }

  1. Devuelve NormalCompletion (vacío).

Este hecho no es fácil de observar, 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, evallo muestra:

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

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

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

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

Las expresiones de función devuelven un objeto de función. Y por eso puedes llamarlo: (function f(){})().

Oriol avatar Oct 09 '2016 19:10 Oriol

Sólo tengo otro pequeño comentario. 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 archivos javascript en vim. Parece que a vim no le gustan las llaves dentro del paréntesis abierto.

Andrei Bozantan avatar Dec 02 '2011 00:12 Andrei Bozantan