ES6 invocó inmediatamente la función de flecha

149

¿Por qué funciona esto en una Node.jsconsola (probado en 4.1.1 y 5.3.0) pero no funciona en el navegador (probado en Chrome)? Este bloque de código debe crear e invocar una función anónima que registre Ok.

() => {
  console.log('Ok');
}()

Además, aunque lo anterior funciona en Node, esto no funciona:

n => {
  console.log('Ok');
}()

Ni esto:

(n) => {
  console.log('Ok');
}()

Lo extraño es que cuando se agrega el parámetro, en realidad arroja un SyntaxErroren la parte que invoca de inmediato.

XCS
fuente
8
Buena pregunta. Ambas versiones parametrizadas funcionan con Babel
CodingIntrigue
2
Fuera de interés, ¿ (n => { console.log("Ok"); })();funciona?
CodingIntrigue
(n => { console.log("Ok"); })()funciona incluso en la consola de desarrollo de Chrome
XCS
y entonces, 3 años después, la respuesta es? seguramente una de las 3 respuestas a continuación debe ser aceptada?
joedotnot
@joedotnot No obtuve una respuesta clara, principalmente fue una implementación extraña en Node.js. Parece que en la última versión de Node.jsla primera versión ya no funciona.
XCS

Respuestas:

194

Debe convertirlo en una expresión de función en lugar de una definición de función que no necesita un nombre y lo convierte en un JavaScript válido.

(() => {
  console.log('Ok');
})()

Es el equivalente de IIFE

(function(){
   console.log('Ok')
})();

Y la posible razón por la que esto funciona en Node.js pero no en Chrome es porque su analizador lo interpreta como una función de ejecución automática, ya que esto

function() { console.log('hello'); }();

funciona bien en Node.jsEsta es una expresión de función y Chrome y Firefox y la mayoría del navegador lo interpreta de esta manera. Necesita invocarlo manualmente.

La forma más ampliamente aceptada de decirle al analizador que espere una expresión de función es simplemente envolverla en parens, porque en JavaScript, los parens no pueden contener declaraciones. En este punto, 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.

En cuanto a la versión parametrizada , esto funcionará.

((n) => {
  console.log('Ok');
})()
vacío
fuente
44
El primer ejemplo funciona Node.jsy realmente registra el valor. Mi pregunta es ¿por qué funciona? ¿Y por qué no cuando agrego el parámetro?
XCS
1
Estoy bastante familiarizado con IIFEsy sé cómo arreglar mi código. Tenía curiosidad por qué, por ejemplo, mi IIFEno funciona cuando nse agrega el parámetro, a pesar de que funcionó sin el parámetro.
XCS
3
No voté en contra, pero la pregunta es por qué la versión parametrizada no funciona en Node cuando la misma definición exacta sin un parámetro sí lo hace: no está preguntando la diferencia entre las implementaciones de funciones anónimas Node / Chrome
CodingIntrigue
1
Es bueno saberlo, pero no responde a la pregunta, como se mencionó anteriormente: ¿por qué la versión parametrizada no funciona en el nodo cuando la misma definición exacta sin un parámetro sí
Jkris
Pero, ¿cuál es el equivalente de function(){}()las funciones de flecha? Si quiero el objeto de función, las expresiones de función expuestas protegen contra eso.
dabadaba
18

Ninguno de estos debería funcionar sin paréntesis.

¿Por qué?

Porque de acuerdo con la especificación:

  1. ArrowFunction está listado bajo AssignmentExpression
  2. El LHS de una CallExpression debe ser MemberExpression , SuperCall o CallExpression

Por lo tanto, una función de flecha no puede estar en el LHS de una expresión de llamada .


Lo que esto significa efectivamente en cómo =>debe interpretarse es que funciona en el mismo tipo de nivel que los operadores de asignación =, +=etc.

Sentido

  • x => {foo}() no se convierte(x => {foo})()
  • El intérprete intenta interpretarlo como x => ({foo}())
  • Por lo tanto, sigue siendo un SyntaxError
  • Entonces el intérprete decide que (debe haber estado equivocado y lanza un SyntaxError

Hubo un error en la Babel de ello aquí , también.

Paul S.
fuente
Esos son algunos puntos válidos, pero si trato de reemplazar la primera versión funcional con: () => ({console.log('Ok')}())ya no funciona. Entonces, en realidad no lo interpreta de esa manera.
XCS
@Cristy No es una producción válida de función de flecha. Piensa que está intentando crear un objeto con literal de objeto (encerrado entre paréntesis) y console.log(...)no es un nombre de clave válido.
thefourtheye
@Cristy: Sí, creo que la parte de interpretación de lo anterior (el bit "Significado") puede no ser del todo correcta, pero las partes de la especificación son, por lo que puedo decir. También se ajusta el error que recibo de V8: SyntaxError: Unexpected token ((señalando el (en el ()en el extremo, no el (de console.log(...)).
TJ Crowder
@TJCrowder tiene razón, lo tacharé, ya que cambia el mensaje de error y lo que estoy tratando de decir no se transmite (es decir, (hizo que el intérprete se rindiera después de agotar los intentos de encontrar una interpretación válida y se va "esto debe estar mal entonces"), lo cual puede estar mal de todos modos porque no sé cómo está realmente escrito el intérprete
Paul S.
Me pregunto si no es una ficha válida en esta posición, ¿no intentaría insertar un punto y coma?
thefourtheye
2

La razón por la que ve problemas como este es porque la consola en sí misma trata de emular el alcance global del contexto al que se dirige actualmente. También intenta capturar los valores de retorno de las declaraciones y expresiones que escribe en la consola, para que aparezcan como resultados. Tomemos, por ejemplo:

> 3 + 2
< 5

Aquí, se ejecuta como si fuera una expresión, pero la ha escrito como si fuera una declaración. En los scripts normales, el valor se descartaría, pero aquí, el código debe ser mutilado internamente (como envolver toda la declaración con un contexto de función y una returndeclaración), lo que causa todo tipo de efectos extraños, incluidos los problemas que está experimentando.

Esta es también una de las razones por las cuales algunos códigos básicos de ES6 en scripts funcionan bien pero no en la consola de Chrome Dev Tools.

Intente ejecutar esto en Node y la consola de Chrome:

{ let a = 3 }

En Node o una <script>etiqueta funciona bien, pero en la consola, da Uncaught SyntaxError: Unexpected identifier. También le proporciona un enlace a la fuente en la VMxxx:1que puede hacer clic para inspeccionar la fuente evaluada, que se muestra como:

({ let a = 3 })

Entonces, ¿por qué hizo esto?

La respuesta es que necesita convertir su código en una expresión para que el resultado pueda ser devuelto a la persona que llama y se muestre en la consola. Puede hacer esto envolviendo la declaración entre paréntesis, lo que la convierte en una expresión, pero también hace que el bloque anterior sea sintácticamente incorrecto (una expresión no puede tener una declaración de bloque).

La consola intenta solucionar estos casos extremos al ser inteligente con el código, pero eso está más allá del alcance de esta respuesta, creo. Puede presentar un error para ver si eso es algo que considerarían solucionar.

Aquí hay un buen ejemplo de algo muy similar:

https://stackoverflow.com/a/28431346/46588

La forma más segura de hacer que su código funcione es asegurarse de que se pueda ejecutar como una expresión e inspeccionar el SyntaxErrorenlace de origen para ver cuál es el código de ejecución real y aplicarle una solución de ingeniería inversa. Por lo general, significa un par de paréntesis estratégicamente ubicados.

En resumen: la consola intenta emular el contexto de ejecución global con la mayor precisión posible, pero debido a las limitaciones de la interacción con el motor v8 y la semántica de JavaScript, esto a veces es difícil o imposible de resolver.

Klemen Slavič
fuente
1
Ese es el punto, me importa el parámetro, pero no funciona con el conjunto de parámetros.
XCS
OK, entiendo tu punto. La diferencia está en la forma en que la consola de Chrome Dev Tools realmente ejecuta su código. Editaré la respuesta para reflejar esto.
Klemen Slavič
0

Hice una pregunta como esta:

@getify Tengo esta pregunta: para producir un patrón #IIFE usamos parans alrededor de una declaración de función para transformarla en una expresión de función y luego invocarla. Ahora en la función de flecha IIFE, ¿por qué necesitamos parans? ¿No es la función de flecha ya una expresión por defecto?

y esta es la respuesta de Kyle Simpson:

una función de flecha es una expr, pero necesitamos parens circundantes b / c de "precedencia de operador" (sorta), de modo que los parens finales para invocar la flecha-IIFE se apliquen a toda la función y no solo al último token de su cuerpo .

x => console.log(x)(4)

vs

(x => console.log(x))(4)

- getify (@getify) 12 de junio de 2020

Ershad Qaderi
fuente
Mi pregunta era por qué funcionaba en algunos compiladores y no en otros.
XCS
Esto se debe a que diferentes compiladores se comportan de manera diferente en algunos detalles, al igual que diferentes navegadores, que por supuesto tienen diferentes compiladores
Ershad Qaderi
Tienes razón, se comportan de manera diferente, pero las especificaciones de JavaScript son las mismas para todos. Tenía curiosidad por saber cuál era el correcto, qué dice la especificación JS sobre este caso y especialmente cómo podría ser que funcione sin argumento pero no con argumento. Estaba buscando una respuesta más técnica.
XCS
Su ejemplo es bastante obvio, en el primer caso debería llamar console.log(x)(4).
XCS
Solo estoy adivinando aquí, pero creo que es muy razonable explicarlo así: en las expresiones de función de flecha cuando no usamos un parámetro debemos usar los parens y eso deja muy claro para el motor que se trata de una flecha expresión de la función, pero cuando tenemos un solo parámetro, los parens son arbitrarios, lo que puede no ser muy claro que sea una función y confunda el motor, para resolver la confusión, tenemos que poner un par de parens alrededor de la expresión de la función completa
Ershad Qaderi