Resumen
¿Puede explicar el razonamiento detrás de la sintaxis de las funciones anónimas encapsuladas en JavaScript? ¿Por qué funciona esto, (function(){})();
pero esto no function(){}();
:?
Lo que yo sé
En JavaScript, uno 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 paréntesis y ejecutándola de inmediato:
(function(){
alert(2 + 2);
})();
Esto es útil al crear scripts modulares, 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 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);
}();
¿Me lo puedes explicar?
fuente
Respuestas:
No funciona porque se analiza como a
FunctionDeclaration
, y el identificador de nombre de las declaraciones de funciones es obligatorio .Cuando lo rodea con paréntesis, se evalúa como a
FunctionExpression
, y las expresiones de función pueden nombrarse o no.La gramática de un se
FunctionDeclaration
ve así:Y
FunctionExpression
s:Como se puede ver el
Identifier
(Identificador opt ) token enFunctionExpression
es opcional, por lo tanto, podemos tener una expresión de función sin un nombre definido:O expresión de función con nombre :
Los paréntesis (formalmente llamados Operador de agrupamiento ) 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 iguales, por ejemplo:
El analizador sabe si es a
FunctionDeclaration
o aFunctionExpression
, según el contexto en el que aparece.En el ejemplo anterior, el segundo es una expresión porque el operador de coma también puede manejar solo expresiones.
Por otro lado,
FunctionDeclaration
s en realidad podría aparecer solo en lo que se llama "Program
" código, que significa código fuera del alcance global y dentroFunctionBody
de otras funciones.Deben evitarse las funciones dentro de los bloques, ya que pueden conducir a un comportamiento impredecible, por ejemplo:
El código anterior en realidad debería producir un
SyntaxError
, ya que unBlock
solo 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
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 Función asignado a la variable multiplicar :
2- Una declaración de función de una función llamada multiplicar :
3- Una expresión de función asignada a la variable multiplica :
4- Una expresión de función con nombre func_name , asignada a la variable multiplica :
fuente
A pesar de que esta es una vieja pregunta y respuesta, discute un tema que hasta el día de hoy arroja a muchos desarrolladores a un ciclo. No puedo contar la cantidad de candidatos de 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 qué es una expresión de función invocada de inmediato.
Sin embargo, me gustaría mencionar que una cosa muy importante es que el fragmento de código de Premasagar no funcionaría incluso si le hubiera dado un identificador de nombre.
La razón por la que esto no funcionaría es porque el motor de JavaScript interpreta esto como una declaración de función seguida de un operador de agrupación completamente no relacionado que no contiene 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.
Otra cosa que me gustaría señalar que puede ser útil 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 desde dentro de la definición de la función en sí.
Por supuesto, usar identificadores de nombre con las definiciones de funciones siempre es útil cuando se trata de depurar código, pero eso es algo completamente diferente ... :-)
fuente
Grandes respuestas ya se han publicado. Pero quiero señalar que las declaraciones de función devuelven un registro de finalización vacío:
Este hecho no es fácil de observar, porque la mayoría de las formas de intentar obtener el valor devuelto convertirá la declaración de función en una expresión de función. Sin embargo, lo
eval
muestra: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 ajusta la función entre paréntesis, se convierte en una expresión de función:
Las expresiones de función devuelven un objeto de función. Y por lo tanto se le puede llamar:
(function f(){})()
.fuente
En javascript, esto se denomina expresión de función invocada inmediatamente (IIFE) .
Para que sea una expresión de función, debe:
encerrarlo usando ()
colocar un operador vacío antes
asignarlo a una variable.
De lo contrario, se tratará como definición de función y no podrá llamarlo / invocarlo al mismo tiempo de la siguiente manera:
Lo anterior te dará un error. Porque solo puede invocar una expresión de función inmediatamente.
Esto se puede lograr de dos maneras: Forma 1:
Camino 2:
Camino 3:
camino 4:
Todo lo anterior invocará inmediatamente la expresión de la función.
fuente
Solo tengo otro pequeño comentario. Su código funcionará con un pequeño cambio:
Uso la sintaxis anterior en lugar de la versión más extendida:
porque no logré que la sangría funcionara correctamente para los archivos javascript en vim. Parece que a vim no le gustan las llaves dentro de paréntesis abiertos.
fuente
function
palabra clave como una declaración de función, en cuyo caso el seguimiento()
se interpreta como un operador de agrupación que, de acuerdo con las reglas de sintaxis de JavaScript, solo puede y debe contener una expresión de JavaScript.()
encerrada dentro del operador de agrupación (la que recomienda Douglas Crockford) para mantener la coherencia: es es común usar IIFE sin asignarlos a una variable, y es fácil olvidar incluir esos paréntesis de ajuste si no los usa de manera consistente.Quizás la respuesta más corta sería que
es una función literal que define una función (anónima). No se espera un par adicional (), que se interpreta como una expresión, a nivel superior, solo literales.
está en una declaración de expresión que invoca una función anónima.
fuente
Lo anterior es una sintaxis válida porque todo lo que se pasa dentro del paréntesis se considera como expresión de función.
Lo anterior no es una sintaxis válida. Debido a que el analizador de sintaxis de script java 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.
fuente
También puedes usarlo como:
o
!
- negación op convierte la definición fn en expresión fn, por lo tanto, puede invocarla inmediatamente con()
. Lo mismo que usar0,fn def
ovoid fn def
fuente
Se pueden usar con parámetros-argumentos como
resultaría como 7
fuente
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 alcance global y el alcance del código real, no se conserva.
fuente