¿Ubicación de paréntesis para la ejecución automática de funciones JavaScript anónimas?

107

Recientemente estaba comparando la versión actual de json2.js con la versión que tenía en mi proyecto y noté una diferencia en cómo se creaba y se ejecutaba la expresión de la función.

El código utilizado para envolver una función anónima entre paréntesis y luego ejecutarla,

(function () {
  // code here
})();

pero ahora envuelve la función ejecutada automáticamente entre paréntesis.

(function () {
  // code here
}());

Hay un comentario de CMS en la respuesta aceptada de Explicar la sintaxis de la función anónima encapsulada de JavaScript que "ambos: (function(){})();y (function(){}());son válidos".

Me preguntaba cuál es la diferencia. ¿El primero toma memoria al dejar una función global y anónima? ¿Dónde debería ubicarse el paréntesis?

Kevin Hakanson
fuente
Relacionado: Sintaxis de invocación de función inmediata (en JSLint)
Bergi
1
Lea también sobre el propósito de esta construcción o consulte una explicación ( técnica ) (también aquí ). Para saber por qué son necesarios los paréntesis, consulte esta pregunta .
Bergi

Respuestas:

66

Son prácticamente iguales.

El primero envuelve una función entre paréntesis para convertirla en una expresión válida y la invoca. El resultado de la expresión no está definido.

El segundo ejecuta la función y los paréntesis alrededor de la invocación automática la convierten en una expresión válida. También se evalúa como indefinido.

No creo que haya una forma "correcta" de hacerlo, ya que el resultado de la expresión es el mismo.

> function(){}()
SyntaxError: Unexpected token (
> (function(){})()
undefined
> (function(){return 'foo'})()
"foo"
> (function(){ return 'foo'}())
"foo"
meder omuraliev
fuente
8
JSLint quiere "(función () {} ());". JSLint dice: "Mueva la invocación a los parens que contienen la función".
XP1
27
En realidad, no está limitado a esos dos, puede usar casi cualquier cosa que haga que el compilador se dé cuenta de que la función es parte de una expresión y no una declaración, como +function(){}()o !function(){}().
Tgr
49
@ XP1: JSLint quiere muchas cosas que sean específicas del estilo de Crockford en lugar de ser sustantivas. Este es uno de ellos.
TJ Crowder
@TJCrowder. ¿Qué recomendarías? jQuery usa el primer estilo y Crockford usa el segundo.
Teej
4
@ThorpeObazee: Realmente no importa, así que haz lo que prefieras. Lo recomendaría contra algunos de los más extraños ( -function(){}();, !function(){}();y básicamente cualquier otro operador justo antes functiontambién funciona, pero me quedaría con las versiones que usan parens). Veo el primero mucho más que el segundo, y es mi preferencia; para mí también tiene más sentido, pero eso es subjetivo. FWIW: jsbin.com/ejaqow
TJ Crowder
13

En ese caso, no importa. Está invocando una expresión que se resuelve en una función en la primera definición y definiendo e invocando inmediatamente una función en el segundo ejemplo. Son similares porque la expresión de la función en el primer ejemplo es solo la definición de la función.

Hay otros casos más obviamente útiles para invocar expresiones que se resuelven en funciones:

(foo || bar)()
Tríptico
fuente
3
Para aclarar a otros lectores (principalmente porque yo mismo no lo entendí al principio :)), foo y / o bar ya deben ser iguales a alguna función. (p foo = function(){alert('hi');}. ej ., si ninguna es una función, se genera un error.
Alexander Bird
3
@AlexanderBird Una aclaración adicional: también arrojará un error si fooes "veraz" pero no una función.
JLRishe
9

No hay ninguna diferencia más allá de la sintaxis.

En cuanto a sus inquietudes sobre el segundo método para hacerlo:

Considerar:

(function namedfunc () { ... }())

namedfuncaún no estará en el ámbito global aunque haya proporcionado el nombre. Lo mismo ocurre con las funciones anónimas. La única forma de conseguirlo en ese ámbito sería asignarlo a una variable dentro de los parens.

((namedfunc = function namedfunc () { ... })())

Los parens externos son innecesarios:

(namedfunc = function namedfunc () { ... })()

Pero no querías esa declaración global de todos modos, ¿verdad?

Entonces se reduce a:

(function namedfunc () { ... })()

Y puede reducirlo aún más: el nombre es innecesario ya que nunca se usará (a menos que su función sea recursiva ... e incluso entonces podría usar arguments.callee)

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

Esa es la forma en que lo pienso (puede que sea incorrecto, todavía no he leído la especificación ECMAScript). Espero eso ayude.

Cristian sanchez
fuente
Tenga en cuenta que arguments.calleeestá obsoleto desde ES5 (y prohibido en modo estricto).
nyuszika7h
"Los parientes externos son innecesarios:" - Creo que evitan errores cuando los archivos están concatenados; de lo contrario, necesitaría un! o algo.
Jimmyt1988
-3

¡La diferencia simplemente existe porque a Douglas Crockford no le gusta el primer estilo para IIFE ! (en serio) ¡¡ Como puedes ver en este video !!.

La única razón para la existencia de la envoltura adicional (){en ambos estilos} es ayudar a hacer esa sección de código Function Expression , porque Function Declaration no se puede llamar inmediatamente. Algunas secuencias de comandos / Minify-ERS uso justo +, !, -y ~en lugar de paréntesis, también. Me gusta esto:

+function() {  
    var foo = 'bar';  
}();

!function() {  
    var foo = 'bar';  
}();

-function() {  
    var foo = 'bar';  
}();

~function() {  
    var foo = 'bar';  
}();

Y todos estos son exactamente los mismos que sus alternativas. La elección entre estos casos es completamente por su cuenta y no hace ninguna diferencia. {Las de ()productos 1 byte del archivo más grande ;-)}

behradkhodayar
fuente