função javascript levando estrondo! sintaxe

204

Eu tenho visto essa sintaxe em algumas bibliotecas agora e estou me perguntando qual é o benefício. (observe que estou ciente dos fechamentos e do que o código está fazendo, estou preocupado apenas com as diferenças sintáticas)

!function(){
  // do stuff
}();

Como alternativa aos mais comuns

(function(){
  // do stuff
})();

para auto-invocar funções anônimas.

Estou pensando em algumas coisas. Primeiro, o que está permitindo que o exemplo principal realmente funcione? Por que o estrondo é necessário para tornar esta afirmação sintaticamente correta? Disseram-me também que +funciona, e tenho certeza que alguns outros, no lugar de!

Segundo, qual é o benefício? Tudo o que posso dizer é que ele salva um único personagem, mas não consigo imaginar que seja um benefício tão grande atrair muitos adotantes. Há algum outro benefício que estou perdendo?

A única outra diferença que vejo é o valor de retorno da função auto-invocadora, mas em ambos os exemplos, não nos importamos com o valor de retorno da função, pois ela é usada apenas para criar um fechamento. Então, alguém pode me dizer por que alguém pode usar a primeira sintaxe?

Brad
fonte
As estruturas gostam de salvar o máximo de caracteres que um minificador não pode otimizar.
28411 alex
O primeiro benefício que me vem à cabeça é que você pode criar uma sandbox como, (function ($) {...}) (jQuery);
JS Taylor
2
ambos os exemplos conseguem isso. Como mencionado, eu entendo o que essas funções estão fazendo, eu estou mais interessado nas diferenças sintáticas
brad
8
Atribuo isso à necessidade, nee, exigência de ser fofo de uma maneira mais sofisticada do que a equipe anterior. "Oh, aqueles pagãos entre parênteses, nós temos !!"
precisa saber é o seguinte
2
Eu nunca soube disso, incrível. Eu gosto do !que enfatiza que está sendo executado.
Cdmckay #

Respostas:

97

Idealmente, você deve ser capaz de fazer tudo isso simplesmente como:

function(){
  // do stuff
}(); 

Isso significa declarar uma função anônima e executá-la. Mas isso não funcionará devido a detalhes da gramática JS.

A forma mais curta de conseguir isso é usar alguma expressão, por exemplo, UnaryExpression (e, portanto, CallExpression):

!function(){
  // do stuff
}(); 

Ou para a diversão:

-function(){
  // do stuff
}(); 

Ou:

+function(){
  // do stuff
}(); 

Ou até:

~function(){
  // do stuff
  return 0;
}( );
sorriso
fonte
3
então o único benefício de concisão?
28411 brad
12
Adicionei
Shaz
5
Expressividade, eu diria. A velocidade não importa realmente nesses casos, pois é executada uma vez.
C-smile #
1
Por curiosidade, notei que nenhum bang e bang apresentam o desempenho mais rápido, mas em algum momento da evolução do Chrome, o bang se tornou mais rápido que o não bang. (Provavelmente estatisticamente insignificante, mas ...) Existe uma razão para isso? Não sei muito sobre o código, mas acho isso bastante fascinante.
Jmk2142
Dois de seus operadores unários podem ser interpretados como binários se um ponto e vírgula estiver ausente. O primeiro e o último são os mais seguros desses exemplos.
73

Em Javascript, functionespera-se que uma linha que comece com seja uma instrução de função e pareça

function doSomething() {
}

Uma função auto-invocável como

function(){
  // do stuff
}();

não se encaixa nesse formulário (e causará um erro de sintaxe no primeiro ponto de abertura porque não há nome da função); portanto, os colchetes são usados ​​para delinear uma expressão de função anônima .

(function(){
  // do stuff
})();

Mas qualquer coisa que crie uma expressão (em oposição a uma instrução de função) fará, portanto, o !. Está dizendo ao intérprete que isso não é uma declaração de função. Fora isso, a precedência do operador determina que a função seja invocada antes da negação.

Eu não estava ciente dessa convenção, mas se ela se tornar comum, poderá contribuir para a legibilidade. O que quero dizer é que qualquer pessoa que esteja lendo !functionno topo de um grande bloco de código esperará uma auto-invocação, da maneira como estamos condicionados a esperar o mesmo quando vemos (function. Exceto que perderemos esses parênteses irritantes. Eu esperava que esse fosse o motivo, em oposição a qualquer economia na velocidade ou na contagem de caracteres.

brainjam
fonte
Essa "linha iniciada com a função é esperada ..." parece bastante confusa. E quanto a isto:var foo = {CR/LF here} function bar() {}
c-smile
45

Além das coisas que já foram ditas, a sintaxe com o! é útil se você escrever javascript sem ponto e vírgula:

var i = 1
!function(){
  console.log('ham')
}()

i = 2
(function(){
  console.log('cheese')
})()

O primeiro exemplo gera 'ham' conforme o esperado, mas o segundo gera um erro porque a instrução i = 2 não é finalizada devido aos seguintes parênteses.

Também em arquivos javascript concatenados, você não precisa se preocupar se o código anterior está com ponto e vírgula ausente. Portanto, não há necessidade do comum; (function () {}) (); para garantir que o seu próprio não se estrague.

Eu sei que minha resposta está meio atrasada, mas acho que ainda não foi mencionada :)

Smoe
fonte
6
+1 para a dica e as memórias ... hoje em dia ninguém gosta de escrever javascript sem ponto e vírgula.
Pablo Grisafi
4
O Twitter Bootstrap é todo JS sem ponto e vírgula, e é bem novo ... (desculpe se o comentário acima era de natureza sarcástica e eu perdi).
brandwaffle
2
Isso não é verdade. Considere o seguinte exemplo:! Function () {console.log ("ham");} ()! Function () {console.log ("cheese")} (); você recebe um erro: "SyntaxError: token inesperado!"
heavysixer
Sim você está certo. Há um erro, se você colocar na mesma linha. Eu não pensei sobre isso. Mas, nenhum script de concatenação / lib que usei até agora faz isso. E quando você minifica e concat, seus arquivos são adicionados ponto e vírgula. Então, honestamente, não consigo imaginar um caso em que você se deparasse com esse problema. Por outro lado, é duas horas aqui e eu poderia ser ignorante :)
Smoe
6

Por um lado, o jsPerf mostra que o uso !(da UnaryExpression) geralmente é mais rápido. Às vezes, eles saem para ser igual, mas quando eles não estão, eu não vi o não-bateu um triunfo muito sobre os outros ainda: http://jsperf.com/bang-function

Isso foi testado no Ubuntu mais recente com o Chrome mais antigo (por exemplo ..), versão 8. Portanto, os resultados podem diferir, é claro.

Edit: Que tal algo louco como delete?

delete function() {
   alert("Hi!"); 
}();

ou void?

void function() {
   alert("Hi!"); 
}();
Shaz
fonte
interessante! Eu consegui 50/50, embora no Safari a velocidade, sem batidas, certamente se mantivesse. Gostaria de saber se há alguma teoria para as possíveis diferenças de velocidade?
28411 brad
@brad Deve haver algo a ver com o safari, se você olhar para os testes, a maioria dos outros navegadores parece estar favorecendo !. Mas eu também gostaria de encontrar uma teoria sobre isso também :)
Shaz
3
i como nula porque é explicitamente dizendo: "Eu não me importo com valor de retorno", e porque me lembra de convenções em outras línguas
code_monk
4

Como você pode ver aqui , a melhor maneira de executar métodos auto-chamados em javascript é usando:

(function(){}()); -> 76,827,475 ops/sec

!function(){}();  -> 69,699,155 ops/sec

(function(){})(); -> 69,564,370 ops/sec
Geku
fonte
1
Duvido que desempenho seja a razão para usar uma sintaxe ou outra. No ops 70M / seg, declarando o fechamento vai ser milhares de vezes mais rápido do que o código dentro de qualquer maneira
Eloims
3

Então, com negar "!" e todos os outros operadores unários como +, -, ~, delete, void, muito foi dito, apenas para resumir:

!function(){
  alert("Hi!");
}(); 

Ou

void function(){
  alert("Hi!");
}();

Ou

delete function(){
  alert("Hi!");
}();

E mais alguns casos com operadores binários por diversão :)

1 > function() {
   alert("Hi!"); 
}();

Ou

1 * function() {
   alert("Hi!"); 
}();

Ou

1 >>> function() {
   alert("Hi!"); 
}();

Ou até

1 == function() {
   alert("Hi!"); 
}();

Deixando o ternário para outra pessoa caras :)

Arman McHitarian
fonte
12
0?0:function() { alert("Hi!"); }();
daniel1426
Bingo, Dan! Temos a um ternário;)
Arman McHitarian