funzione javascript leader bang! sintassi


204

Ho visto questa sintassi su alcune librerie ora e mi chiedo quale sia il vantaggio. (nota che sono ben consapevole delle chiusure e di ciò che il codice sta facendo, sono preoccupato solo per le differenze sintattiche)

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

In alternativa al più comune

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

per funzioni anonime auto invocanti.

Mi chiedo alcune cose. Prima di tutto, cosa sta permettendo all'esempio principale di funzionare davvero? Perché il botto è necessario per rendere sintatticamente corretta questa affermazione? Mi è stato anche detto che +funziona, e sono sicuro che alcuni altri, al posto di!

Secondo, qual è il vantaggio? Tutto quello che posso dire è che salva un singolo personaggio, ma non riesco a immaginare che sia un enorme vantaggio per attirare numerosi utenti. C'è qualche altro vantaggio che mi manca?

L'unica altra differenza che posso vedere sarebbe il valore di ritorno della funzione auto-invocante, ma in entrambi questi esempi, non ci interessa davvero il valore di ritorno della funzione poiché viene utilizzato solo per creare una chiusura. Quindi qualcuno può dirmi perché si potrebbe usare la prima sintassi?


Ai quadri piace salvare quanti più caratteri possibile che un minificatore non può ottimizzare.
alex

Il primo vantaggio mi viene in mente è che puoi creare un sandbox come, (function ($) {...}) (jQuery);
JS Taylor,

2
entrambi gli esempi ottengono questo risultato. Come detto, capisco cosa stanno facendo queste funzioni, sono più interessato alle differenze sintattiche
Brad

8
Lo attribuisco alla necessità, quindi, di essere carino in un modo più sofisticato rispetto al precedente equipaggio. "Oh, quei pagenati tra parentesi, abbiamo !!"
Jared Farrish,

2
Non l'ho mai saputo, fantastico. Mi piace il modo !in cui sottolinea che viene eseguito.
cdmckay,

Risposte:


97

Idealmente dovresti essere in grado di fare tutto questo semplicemente come:

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

Ciò significa dichiarare la funzione anonima ed eseguirla. Ma ciò non funzionerà a causa dei dettagli della grammatica JS.

La forma più breve per raggiungere questo obiettivo è usare alcune espressioni, ad esempio UnaryExpression (e quindi CallExpression):

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

O per divertimento:

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

O:

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

O anche:

~function(){
  // do stuff
  return 0;
}( );

3
quindi quindi l' unico beneficio terseness?
Brad

12
Ho aggiunto tutte le opzioni sopra al test delle prestazioni su: jsperf.com/bang-function
Shaz

5
Espressività direi. La velocità non ha davvero importanza in questi casi in quanto viene eseguita una volta.
c-smile,

1
Per curiosità, ho notato che nessun bang and bang si comporta meglio, ma almeno nell'evoluzione di Chrome, il bang è diventato più veloce di nessun bang. (Probabilmente statisticamente insignificante ma ...) C'è un motivo per cui? Non so molto sul codice sotto il cofano, ma lo trovo abbastanza affascinante.
jmk2142,

Due dei tuoi operatori unari possono essere interpretati come binari se manca un punto e virgola. Il primo e l'ultimo sono gli esempi più sicuri.

73

In Javascript, una riga che inizia con functiondovrebbe essere un'istruzione di funzione e dovrebbe apparire come

function doSomething() {
}

Una funzione auto-invocante come

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

non si adatta a quel modulo (e causerà un errore di sintassi alla prima apertura perché non esiste un nome di funzione), quindi le parentesi vengono utilizzate per delineare un'espressione di funzione anonima .

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

Ma tutto ciò che crea un'espressione (al contrario di un'istruzione di funzione) lo farà, quindi da qui !. Sta dicendo all'interprete che questa non è un'istruzione di funzione. Oltre a ciò, la precedenza dell'operatore impone che la funzione sia invocata prima della negazione.

Non ero a conoscenza di questa convenzione, ma se diventa comune può contribuire alla leggibilità. Ciò che intendo è che chiunque legga la !functionparte superiore di un grande blocco di codice si aspetterà un'auto-invocazione, il modo in cui siamo già condizionati ad aspettarci lo stesso quando vediamo (function. Solo che perderemo quelle fastidiose parentesi. Mi aspetto che questo sia il motivo, al contrario di eventuali risparmi nella velocità o nel conteggio dei personaggi.


Questa "linea che inizia con la funzione è prevista ..." sembra piuttosto sfocata. Che dire di questo:var foo = {CR/LF here} function bar() {}
c-smile,

45

Oltre alle cose che sono già state dette, la sintassi con! è utile se scrivi javascript senza punto e virgola:

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

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

Il primo esempio genera "ham" come previsto, ma il secondo genererà un errore perché l'istruzione i = 2 non è terminata a causa della seguente parentesi.

Anche nei file javascript concatenati non devi preoccuparti se nel codice precedente mancano i punti e virgola. Quindi non c'è bisogno del comune; (function () {}) (); per assicurarti che il tuo non si rompa.

So che la mia risposta è un po 'in ritardo ma penso che non sia ancora stata menzionata :)


6
+1 per il suggerimento e i ricordi ... a nessuno piace scrivere javascript senza punti e virgola in questi giorni.
Pablo Grisafi,

4
Twitter Bootstrap è tutto JS senza punto e virgola, ed è piuttosto nuovo ... (scusate se il commento sopra era di natura sarcastica e me lo sono perso).
brandwaffle,

2
Questo non è in realtà vero. Considera il seguente esempio:! Function () {console.log ("ham");} ()! Function () {console.log ("cheese")} (); viene visualizzato un errore: "SyntaxError: token imprevisto!"
heavysixer

Si hai ragione. C'è un errore, se li metti sulla stessa riga. Non ci ho pensato. Ma nessuno script di concatenazione / lib che ho usato finora lo fa. E quando minimizzi e concateni i tuoi file vengono aggiunti i punti e virgola. Quindi, onestamente, non riesco a immaginare un caso in cui potresti incontrare questo problema. D'altra parte, sono le 2 del mattino e potrei essere ignorante :)
Smoe,

6

Per prima cosa, jsPerf mostra che l'utilizzo di !(UnaryExpression's) è generalmente più veloce. A volte risultano uguali, ma quando non lo sono, non ho ancora visto il trionfo di un non sbattuto troppo sugli altri: http://jsperf.com/bang-function

Questo è stato testato sull'ultimo Ubuntu con il più vecchio (per esempio ..) Chrome, versione 8. Quindi i risultati possono ovviamente differire.

Modifica: che ne dici di qualcosa di pazzo come delete?

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

o void?

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

interessante! Ho ottenuto circa il 50/50 anche se in Safari per quanto riguarda la velocità, il non sbattuto ha sicuramente tenuto il suo. Mi chiedo se c'è qualche teoria sulle potenziali differenze di velocità?
Brad

@brad Deve essere qualcosa a che fare con Safari, se guardi i test, la maggior parte degli altri browser sembra favorire !. Ma vorrei anche trovare una teoria anche su questo :)
Shaz,

3
mi piace il nulla perché dice esplicitamente "non mi interessa il valore di ritorno" e perché mi ricorda convenzioni in altre lingue
code_monk

4

Come puoi vedere qui , il modo migliore per eseguire metodi auto-invocati in JavaScript è:

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

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

(function(){})(); -> 69,564,370 ops/sec

1
Dubito che le prestazioni siano il motivo per usare una sintassi o l'altra. A 70M ops / sec, dichiarando che la chiusura sarà comunque migliaia di volte più veloce del codice interno
Eloims

3

Quindi, con negazione "!" e tutti gli altri operatori unari come +, -, ~, delete, void, molto è stato detto, solo per riassumere:

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

O

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

O

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

E qualche altro caso con operatori binari per divertimento :)

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

O

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

O

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

O anche

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

Lasciando il ternario per qualcun altro ragazzi :)


12
0?0:function() { alert("Hi!"); }();
daniel1426

Bingo, Dan! Abbiamo quello ternario;)
Arman McHitarian
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.