Localização do parêntese para funções JavaScript anônimas de execução automática?

107

Recentemente, estava comparando a versão atual do json2.js com a versão que tinha em meu projeto e notei uma diferença em como a expressão de função foi criada e executada automaticamente .

O código usado para envolver uma função anônima entre parênteses e, em seguida, executá-la,

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

mas agora ele envolve a função executada automaticamente entre parênteses.

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

Há um comentário do CMS na resposta aceita da sintaxe da função anônima encapsulada do Explain JavaScript que "ambos: (function(){})();e (function(){}());são válidos".

Eu queria saber qual é a diferença? O primeiro ocupa memória, deixando uma função global anônima? Onde o parêntese deve ser localizado?

Kevin Hakanson
fonte
Relacionado: Sintaxe de invocação de função imediata (em JSLint)
Bergi
1
Leia também sobre o propósito desta construção ou verifique uma explicação ( técnica ) (também aqui ). Para saber por que os parênteses são necessários, consulte esta pergunta .
Bergi

Respostas:

66

Eles são virtualmente os mesmos.

O primeiro envolve parênteses em torno de uma função para torná-la uma expressão válida e a invoca. O resultado da expressão é indefinido.

O segundo executa a função e os parênteses em torno da invocação automática tornam-na uma expressão válida. Ele também é avaliado como indefinido.

Não acho que haja uma maneira "certa" de fazer isso, já que o resultado da expressão é o mesmo.

> function(){}()
SyntaxError: Unexpected token (
> (function(){})()
undefined
> (function(){return 'foo'})()
"foo"
> (function(){ return 'foo'}())
"foo"
Meder Omuraliev
fonte
8
JSLint deseja "(function () {} ());". JSLint diz: "Mova a invocação para os parênteses que contêm a função."
XP1
27
Na verdade, você não está limitado a esses dois, você pode usar praticamente qualquer coisa que faça o compilador perceber que a função é parte de uma expressão e não de uma instrução, como +function(){}()ou !function(){}().
Tgr
49
@ XP1: JSLint deseja muitas coisas que sejam específicas ao estilo de Crockford, em vez de substantivas. Este é um deles.
TJ Crowder
@TJCrowder. O que você recomendaria? jQuery usa o primeiro estilo e Crockford usa o segundo.
Teej
4
@ThorpeObazee: Realmente não importa, então faça o que preferir. Eu recomendaria contra alguns dos mais extravagantes ( -function(){}();, !function(){}();e basicamente qualquer outro operador imediatamente anterior functiontambém funciona, mas preferiria as versões que usam parênteses). Vejo o primeiro muito mais do que vejo o segundo, e é minha preferência; faz mais sentido para mim também, mas isso é subjetivo. FWIW: jsbin.com/ejaqow
TJ Crowder
13

Nesse caso, não importa. Você está invocando uma expressão que resolve para uma função na primeira definição e definindo e invocando imediatamente uma função no segundo exemplo. Eles são semelhantes porque a expressão de função no primeiro exemplo é apenas a definição da função.

Existem outros casos mais obviamente úteis para invocar expressões que resolvem em funções:

(foo || bar)()
Tríptico
fonte
3
Para esclarecimento a outros leitores (principalmente porque eu mesmo não entendia no começo :)), foo e / ou bar já devem ser iguais a alguma função. (por exemplo, foo = function(){alert('hi');}se nenhuma das funções for uma função, será gerado um erro.
Alexander Bird
3
@AlexanderBird Um esclarecimento adicional - Também irá gerar um erro se foofor "verdadeiro", mas não uma função.
JLRishe de
9

Não há nenhuma diferença além da sintaxe.

Em relação às suas preocupações sobre o segundo método de fazer isso:

Considerar:

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

namedfuncainda não estará no escopo global, embora você tenha fornecido o nome. O mesmo vale para funções anônimas. A única maneira de colocá-lo nesse escopo seria atribuí-lo a uma variável dentro dos parênteses.

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

Os parênteses externos são desnecessários:

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

Mas você não queria essa declaração global de qualquer maneira, queria?

Então, tudo se resume a:

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

E você pode reduzir ainda mais: o nome é desnecessário, pois nunca será usado (a menos que sua função seja recursiva .. e mesmo assim você poderia usar arguments.callee)

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

É assim que penso (pode estar incorreto, ainda não li a especificação ECMAScript). Espero que ajude.

Cristian Sanchez
fonte
Observe que arguments.calleeestá obsoleto desde ES5 (e proibido no modo estrito).
nyuszika7h
"Os parênteses externos são desnecessários:" - Acho que eles evitam erros quando os arquivos são concatenados, caso contrário, você precisaria de um! ou alguma coisa.
Jimmyt1988
-3

A diferença existe porque Douglas Crockford não gosta do primeiro estilo para IIFEs ! (sério) Como você pode ver neste vídeo !! .

A única razão para a existência do invólucro extra (){em ambos os estilos} é ajudar a fazer essa seção do código Expressão de Função , porque a Declaração de Função não pode ser chamada imediatamente. Alguns scripts / minify-ers uso apenas +, !, -e ~em vez de também parênteses. Como isso:

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

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

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

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

E tudo isso é exatamente igual às suas alternativas. A escolha entre esses casos é totalmente por sua conta e não faz diferença. {Aqueles que ()produzem um arquivo maior de 1 byte ;-)}

Behradkhodayar
fonte