ES6 chamou imediatamente a função de seta

149

Por que isso funciona em um Node.jsconsole (testado em 4.1.1 e 5.3.0) mas não funciona no navegador (testado no Chrome)? Esse bloco de código deve criar e chamar uma função anônima que faça logon Ok.

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

Além disso, enquanto o acima funciona no Node, isso não funciona:

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

Nem isso:

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

O que é estranho é que, quando o parâmetro é adicionado, ele realmente lança um SyntaxErrorna parte que chama imediatamente.

XCS
fonte
8
Boa pergunta. Ambas as versões parametrizadas trabalhar com Babel
CodingIntrigue
2
Fora de interesse, (n => { console.log("Ok"); })();funciona?
precisa saber é o seguinte
Sim (n => { console.log("Ok"); })()funciona mesmo no console do desenvolvedor do Chrome
XCS 04/01
e então, 3 anos depois, a resposta é? certamente uma das 3 respostas abaixo deve ser aceita ?!
Joedotnot 30/10/19
@joedotnot Não recebi uma resposta clara, principalmente uma implementação estranha no Node.js. Parece que a versão mais recente da Node.jsprimeira versão não está mais funcionando.
XCS

Respostas:

194

Você precisa transformá-la em uma expressão de função em vez de na definição de função, que não precisa de um nome e o torna um JavaScript válido.

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

É o equivalente a IIFE

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

E a possível razão pela qual isso funciona no Node.js, mas não no chrome, é porque seu analisador o interpreta como uma função de execução automática, pois

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

funciona bem em Node.jsEsta é uma expressão de função e chrome e firefox e a maior parte do navegador interpreta dessa maneira. Você precisa invocá-lo manualmente.

A maneira mais aceita de dizer ao analisador que espere uma expressão de função é envolvê-la em parênteses, porque no JavaScript, parênteses não podem conter instruções. Nesse ponto, quando o analisador encontra a palavra-chave function, ele sabe analisá-la como uma expressão de função e não como uma declaração de função.

Em relação à versão parametrizada , isso funcionará.

((n) => {
  console.log('Ok');
})()
vazio
fonte
4
O primeiro exemplo funciona Node.jse efetivamente registra o valor. Minha pergunta é por que isso funciona? E por que não acontece quando adiciono o parâmetro?
XCS 4/16
1
Estou bastante familiarizado com IIFEs e sei como corrigir meu código. Fiquei curioso por que, por exemplo, meu IIFEnão funciona quando o nparâmetro é adicionado, mesmo que funcionasse sem o parâmetro.
XCS
3
Eu não downvote, mas a questão é por que a versão com parâmetros não trabalho no nó quando a exata mesma definição sem um parâmetro faz - não está pedindo a diferença entre implementações Node / Chrome de funções anônimas
CodingIntrigue
1
É uma boa de saber, mas ele não responder à pergunta, como mencionado antes, - por que a versão com parâmetros não trabalho no nó quando a exata mesma definição sem um parâmetro faz
jkris
Mas qual é o equivalente function(){}()nas funções de seta? Se eu quiser que o objeto de função exposto, as expressões de função se protejam contra isso.
dabadaba
18

Nada disso deve funcionar sem parênteses.

Por quê?

Porque de acordo com as especificações:

  1. ArrowFunction está listado em AssignmentExpression
  2. Os LHS de um CallExpression deve ser um MemberExpression , SuperCall ou CallExpression

Portanto, uma ArrowFunction não pode estar no LHS de uma CallExpression .


O que isso significa efetivamente em como =>deve ser interpretado é que ele funciona no mesmo tipo de nível que os operadores de atribuição =, +=etc.

Significado

  • x => {foo}() não se torna(x => {foo})()
  • O intérprete tenta interpretá-lo como x => ({foo}())
  • Portanto, ainda é um SyntaxError
  • Portanto, o intérprete decide que (deve estar errado e lança um SyntaxError

Também houve um bug em Babel aqui .

Paul S.
fonte
Esses são alguns pontos válidos, mas se eu tentar substituir a primeira versão de trabalho por: () => ({console.log('Ok')}())não funciona mais. Portanto, ele realmente não interpreta dessa maneira.
XCS 4/16
@Cristy Não é uma produção válida da função Arrow. Ele pensa que você está tentando criar um literal Objeto com Objeto (entre parênteses) e console.log(...)não é um nome de chave válido.
thefourtheye
@ Christy: Sim, acho que a parte da interpretação acima (o bit "Meaning") pode não estar muito correta, mas as partes da especificação estão tão longe quanto eu posso dizer. Também se encaixa no erro que recebo da V8: SyntaxError: Unexpected token ((apontando para o (no ()final e não o (interno console.log(...)).
TJ Crowder
@TJCrowder você está certo, mostrarei isso à medida que altera a mensagem de erro e o que estou tentando dizer não é transmitido (ou seja, (fez o intérprete desistir após tentativas exaustivas de encontrar uma interpretação válida e continuar "este deve ser errado, então"), que pode ser de qualquer maneira errada, porque eu não sei como o intérprete é realmente escrita
Paul S.
Gostaria de saber se não é um token válido nessa posição, ele não tentaria inserir um ponto e vírgula?
thefourtheye
2

A razão pela qual você está enfrentando problemas como esse é que o próprio console tenta emular o escopo global do contexto que você está direcionando atualmente. Ele também tenta capturar valores de retorno de instruções e expressões que você escreve no console, para que apareçam como resultados. Tomemos, por exemplo:

> 3 + 2
< 5

Aqui, ele é executado como se fosse uma expressão, mas você a escreveu como se fosse uma declaração. Em scripts normais, o valor seria descartado, mas aqui, o código deve ser mutilado internamente (como agrupar toda a instrução com um contexto de função e uma returninstrução), o que causa todos os tipos de efeitos estranhos, incluindo os problemas que você está enfrentando.

Esse também é um dos motivos pelos quais alguns códigos ES6 vazios nos scripts funcionam bem, mas não no console das Ferramentas de Desenvolvimento do Chrome.

Tente executar isso no console do Node e do Chrome:

{ let a = 3 }

No Node ou em uma <script>tag, ele funciona muito bem, mas no console, ele fornece Uncaught SyntaxError: Unexpected identifier. Também fornece um link para a fonte no formato no VMxxx:1qual você pode clicar para inspecionar a fonte avaliada, que aparece como:

({ let a = 3 })

Então, por que fez isso?

A resposta é que ele precisa converter seu código em uma expressão para que o resultado possa ser retornado ao chamador e exibido no console. Você pode fazer isso colocando a instrução entre parênteses, o que a torna uma expressão, mas também torna o bloco acima sintaticamente incorreto (uma expressão não pode ter uma declaração de bloco).

O console tenta corrigir esses casos extremos, sendo esperto quanto ao código, mas acho que está além do escopo desta resposta. Você pode registrar um bug para ver se isso é algo que eles considerariam consertar.

Aqui está um bom exemplo de algo muito semelhante:

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

A maneira mais segura de fazer seu código funcionar é garantir que ele possa ser executado como uma expressão e inspecionar o SyntaxErrorlink de origem para ver qual é o código de execução real e fazer a engenharia reversa de uma solução a partir disso. Geralmente significa um par de parênteses estrategicamente posicionados.

Resumindo: o console tenta emular o contexto de execução global com a maior precisão possível, mas devido às limitações de interação com o mecanismo v8 e a semântica do JavaScript, isso às vezes é difícil ou impossível de resolver.

Klemen Slavič
fonte
1
Esse é o ponto, eu me preocupo com o parâmetro, mas ele não funciona com o conjunto de parâmetros.
XCS
OK, entendo o seu ponto. A diferença está na maneira como o console das Ferramentas de Desenvolvimento do Chrome realmente executa seu código. Vou editar a resposta para refletir isso.
Klemen Slavič
0

Eu fiz uma pergunta como esta:

@getify Eu tenho essa pergunta: para produzir um padrão #IIFE, usamos parans em torno de uma declaração de função para transformá-lo em uma expressão de função e depois invocá-lo. Agora, na função de seta IIFEs, por que precisamos de parans ?! A função de seta já não é uma expressão por padrão ?!

e esta é a resposta de Kyle Simpson:

uma função de flecha é uma expr, mas precisamos de parênteses b / c de "precedência do operador" (sorta), de modo que as parênteses finais para invocar a seta-IIFE se apliquem a toda a função e não apenas ao último símbolo de seu corpo .

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

vs

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

- getify (@getify) 12 de junho de 2020

Ershad Qaderi
fonte
Minha pergunta era por que funcionou em alguns compiladores e não em outros.
XCS
Isso porque compiladores diferentes se comportam de maneira diferente em alguns detalhes, assim como navegadores diferentes, que obviamente têm compiladores diferentes
Ershad Qaderi
Você está certo, eles se comportam de maneira diferente, mas as especificações do JavaScript são as mesmas para todos eles. Fiquei curioso para saber qual estava certo, o que as especificações do JS dizem sobre este caso e, principalmente, como é possível que ele funcione sem argumentos, mas não com argumentos. Eu estava procurando uma resposta mais técnica.
XCS
Seu exemplo é bastante óbvio, no primeiro caso, ele deveria chamar console.log(x)(4).
XCS
Estou apenas adivinhando aqui, mas acho muito razoável explicá-lo assim: nas expressões de função de seta quando não usamos um parâmetro, devemos usar os parênteses e isso deixa muito claro para o mecanismo que esta é uma seta expressão de função, mas quando temos um único parâmetro, os parênteses são arbitrários, o que pode não estar muito claro se essa é uma função e confunde o mecanismo, para resolver a confusão, precisamos colocar um par de parênteses em torno de toda a expressão de função
Ershad Qaderi 15/06