Explicar a syntax da function anônima encapsulada

Resumo

Você pode explicar o raciocínio por trás da syntax de funções anônimas encapsuladas em JavaScript? Por que isso funciona: (function(){})(); mas isso não function(){}(); : function(){}(); ?


O que eu sei

Em JavaScript, cria-se uma function nomeada como esta:

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

Você também pode criar uma function anônima e atribuí-la a uma variável:

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

Você pode encapsular um bloco de código criando uma function anônima, colocando-a entre colchetes e executando-a imediatamente:

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

Isso é útil ao criar scripts modularizados, para evitar sobrecarregar o escopo atual ou o escopo global, com variables ​​potencialmente conflitantes – como no caso de scripts do Greasemonkey, plug-ins do jQuery etc.

Agora eu entendo porque isso funciona. Os colchetes encerram o conteúdo e expõem apenas o resultado (tenho certeza de que há uma maneira melhor de descrever isso), como com (2 + 2) === 4 .


O que eu não entendo

Mas eu não entendo porque isso não funciona igualmente bem:

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

Você pode explicar isso para mim?

Ele não funciona porque está sendo analisado como uma declaração de function e o identificador de nome das declarações de function é obrigatório .

Quando você o cerca com parênteses, ele é avaliado como um FunctionExpression e as expressões de function podem ser nomeadas ou não.

A gramática de uma declaração de FunctionDeclaration é assim:

 function Identifier ( FormalParameterListopt ) { FunctionBody } 

E FunctionExpression s:

 function Identifieropt ( FormalParameterListopt ) { FunctionBody } 

Como você pode ver o token Identifier (Identifier opt ) em FunctionExpression é opcional, portanto podemos ter uma expressão de function sem um nome definido:

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

Ou a expressão da function nomeada :

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

Os Parênteses (formalmente chamados de Operador de Agrupamento ) podem cercar apenas expressões, e uma expressão de function é avaliada.

As duas produções gramaticais podem ser ambíguas e podem ser exatamente iguais, por exemplo:

 function foo () {} // FunctionDeclaration 0,function foo () {} // FunctionExpression 

O analisador sabe se é um FunctionDeclaration ou um FunctionExpression , dependendo do contexto em que aparece.

No exemplo acima, o segundo é uma expressão porque o operador Comma também pode manipular somente expressões.

Por outro lado, FunctionDeclaration s pode aparecer apenas no chamado código ” Program “, significando código fora do escopo global e dentro do FunctionBody de outras funções.

Funções dentro de blocos devem ser evitadas, porque elas podem levar a um comportamento imprevisível, por exemplo:

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

O código acima deve realmente produzir um SyntaxError , já que um Block pode conter apenas instruções (e a especificação ECMAScript não define nenhuma instrução de function), mas a maioria das implementações é tolerante e simplesmente pega a segunda function, aquela que alerta 'false!' .

As implementações do Mozilla –Rhino, SpiderMonkey – têm um comportamento diferente. Sua gramática contém uma declaração de function não padrão , o que significa que a function será avaliada em tempo de execução , não em tempo de análise, como acontece com FunctionDeclaration s. Nessas implementações, obteremos a primeira function definida.


Funções podem ser declaradas de maneiras diferentes, compare o seguinte :

1- Uma function definida com o construtor Function atribuído à variável multiply :

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

2- Uma declaração de function de uma function chamada multiplicar :

 function multiply(x, y) { return x * y; } 

3- Uma expressão de function atribuída à variável multiplicar :

 var multiply = function (x, y) { return x * y; }; 

4- Uma expressão de function nomeada func_name , atribuída à variável multiplicar :

 var multiply = function func_name(x, y) { return x * y; }; 

Embora essa seja uma pergunta e uma resposta antigas, ela discute um tópico que até hoje gera muitos desenvolvedores para um loop. Não posso contar o número de candidatos a desenvolvedor de JavaScript que entrevistei que não puderam me dizer a diferença entre uma declaração de function e uma expressão de function e que não tinham idéia do que é uma expressão de function imediatamente invocada.

Eu gostaria de mencionar, no entanto, uma coisa muito importante, que é que o trecho de código do Premasagar não funcionaria mesmo se ele tivesse dado um identificador de nome.

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

A razão pela qual isso não funcionaria é que o mecanismo JavaScript interpreta isso como uma declaração de function seguida por um operador de agrupamento completamente não relacionado que não contém expressão, e os operadores de agrupamento devem conter uma expressão. De acordo com o JavaScript, o trecho de código acima é equivalente ao seguinte.

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

Outra coisa que gostaria de salientar que pode ser de alguma utilidade para algumas pessoas é que qualquer identificador de nome que você fornecer para uma expressão de function é praticamente inútil no contexto do código, exceto a partir da própria definição da function.

 var a = function b() { // do something }; a(); // works b(); // doesn't work var c = function d() { window.setTimeout(d, 1000); // works }; 

Claro, usar identificadores de nome com suas definições de function é sempre útil quando se trata de depurar código, mas isso é algo totalmente diferente … 🙂

Grandes respostas já foram postadas. Mas quero observar que as declarações de function retornam um registro de conclusão vazio:

14.1.20 – Semântica de tempo de execução: avaliação

FunctionDeclaration : function BindingIdentifier ( FormalParameters ) { FunctionBody }

  1. Retorna NormalCompletion (vazio).

Esse fato não é fácil de observar, porque a maioria das maneiras de tentar obter o valor retornado irá converter a declaração de function em uma expressão de function. No entanto, eval mostra isso:

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

Em javascript, isso é chamado Expressão de Função Imediatamente Invocada (IIFE) .

Para torná-la uma expressão de function, você precisa:

  1. coloque-o usando ()

  2. colocar um operador vazio antes dele

  3. atribuí-lo a uma variável.

Caso contrário, ele será tratado como definição de function e, em seguida, você não poderá chamá-lo / invocá-lo ao mesmo tempo da seguinte maneira:

  function (arg1) { console.log(arg1) }(); 

O acima lhe dará erro. Porque você só pode invocar uma expressão de function imediatamente.

Isso pode ser conseguido de duas maneiras: Way 1:

 (function(arg1, arg2){ //some code })(var1, var2); 

Caminho 2:

 (function(arg1, arg2){ //some code }(var1, var2)); 

Caminho 3:

 void function(arg1, arg2){ //some code }(var1, var2); 

caminho 4:

  var ll = function (arg1, arg2) { console.log(arg1, arg2); }(var1, var2); 

Tudo acima irá invocar imediatamente a expressão de function.

Eu tenho apenas outra pequena observação. Seu código funcionará com uma pequena alteração:

 var x = function(){ alert(2 + 2); }(); 

Eu uso a syntax acima, em vez da versão mais amplamente difundida:

 var module = (function(){ alert(2 + 2); })(); 

porque eu não consegui fazer o recuo funcionar corretamente para arquivos javascript no vim. Parece que o vim não gosta das chaves dentro de parênteses abertos.

Talvez a resposta mais curta seria

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

é um literal de function que define uma function (anônima). Um par adicional (), que é interpretado como uma expressão, não é esperado no nível superior, apenas literais.

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

está em uma declaração de expressão que chama uma function anônima.

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

Acima é uma syntax válida porque qualquer coisa passada entre parênteses é considerada como expressão de function.

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

Acima não é uma syntax válida. Como o analisador de syntax do script java procura nome da function após a palavra-chave da function, uma vez que não encontra nada, ele gera um erro.

Eles podem ser usados ​​com argumentos de parâmetros como

 var x = 3; var y = 4; (function(a,b){alert(a + b)})(x,y) 

resultaria como 7

Esses parênteses extras criam funções anônimas adicionais entre espaço de nomes global e function anônima que contém o código. E em funções JavaScript declaradas dentro de outras funções só pode acessar o namespace da function pai que as contém. Como há object extra (function anônima) entre o escopo global e o escopo real do código não é retido.