Cosa fa il punto esclamativo prima della funzione?


1242
!function () {}();

4
@Dykam La sua utilità è spiegato in questa risposta: stackoverflow.com/questions/5422585/...
hectorct



7
@befzz Meglio riferirsi a questo come a un'espressione di funzione immediatamente invocata, come spiega in seguito quell'articolo ("auto-esecuzione" implica la ricorsione)
Zach Esposito

Risposte:


2118

Sintassi JavaScript 101. Ecco una dichiarazione di funzione :

function foo() {}

Nota che non c'è punto e virgola: questa è solo una dichiarazione di funzione . Avresti bisogno di una chiamata foo(), per eseguire effettivamente la funzione.

Ora, quando aggiungiamo il punto esclamativo apparentemente innocuo: !function foo() {}lo trasforma in un'espressione . Ora è un'espressione di funzione .

Il !solo non invoca la funzione, ovviamente, ma ora possiamo mettere ()alla fine: !function foo() {}()che ha una precedenza più alta di !e chiama immediatamente la funzione.

Quindi ciò che l'autore sta facendo è salvare un byte per espressione di funzione; un modo più leggibile di scrivere sarebbe questo:

(function(){})();

Infine, !restituisce l'espressione vera. Questo perché di default tutto il ritorno IIFE undefined, che ci lascia con ciò !undefinedche è true. Non particolarmente utile.


229
+1. Questa è davvero la migliore risposta qui, e purtroppo, a malapena votata. Ovviamente, !restituisce il valore booleano, lo sappiamo tutti, ma il punto importante che fai è che converte anche l'istruzione dichiarazione di funzione in un'espressione di funzione in modo che la funzione possa essere immediatamente invocata senza racchiuderla tra parentesi. Non ovvio, e chiaramente l'intento del programmatore.
gilly3,

64
+1 Questa è l'unica risposta che in realtà si rivolge PERCHÉ vorresti farlo, e perché uno lo vede usato più della negazione del risultato di ritorno sembrerebbe giustificare. L'operatore unario! (anche ~, - e +) si discambia da una dichiarazione di funzione e consente alle parentesi alla fine () di invocare la funzione sul posto. Questo viene spesso fatto per creare un ambito / spazio dei nomi locale per le variabili durante la scrittura di codice modulare.
Tom Auger,

65
Un altro vantaggio è quello! causa un inserimento di punti e virgola, quindi è impossibile concatenare erroneamente questa versione con un file che non termina con un;. Se si dispone del modulo (), lo considererebbe una chiamata di funzione di qualsiasi cosa sia stata definita nel file precedente. Punta del cappello a un mio collega.
Jure Triglav,

5
@Carnix var foo =rompe l'ambiguità dell'affermazione / espressione e puoi semplicemente scrivere var foo = function(bar){}("baz");ecc.
Neil

6
Questo di solito è fatto da script di minificazione / uglificazione, in cui conta ogni singolo byte.
Dima Slivin,

367

La funzione:

function () {}

non restituisce nulla (o non definito).

A volte vogliamo chiamare una funzione nel momento in cui la creiamo. Potresti essere tentato di provare questo:

function () {}()

ma risulta in a SyntaxError.

L'uso !dell'operatore prima della funzione fa sì che venga trattata come un'espressione, quindi possiamo chiamarla:

!function () {}()

Ciò restituirà anche l'opposto booleano del valore restituito della funzione, in questo caso true, perché !undefinedè true. Se desideri che il valore restituito effettivo sia il risultato della chiamata, prova a farlo in questo modo:

(function () {})()

28
chi potrà mai aver bisogno di questo?
Andrey,

13
questa è l' unica risposta che spiega il caso nella domanda, bravo!
Andrey,

14
Il tuo secondo esempio di codice non è JavaScript valido. Lo scopo di !è trasformare la dichiarazione di funzione in un'espressione di funzione, tutto qui.
Skilldrick,

8
@Andrey Il bootstrap twitter lo usa in tutti i file plugin javascript (jQuery). Aggiungendo questo commento nel caso in cui anche altri abbiano la stessa domanda.
Anmol Saraf,

2
d3.js utilizza anche la !functionsintassi
Kristian

65

C'è un buon punto per usare !per l'invocazione della funzione segnata sulla guida JavaScript di airbnb

Generalmente idea di utilizzare questa tecnica su file separati (aka moduli) che successivamente vengono concatenati. L'avvertenza qui è che i file dovrebbero essere concatenati da strumenti che mettono il nuovo file sulla nuova riga (che è comunque un comportamento comune per la maggior parte degli strumenti concat). In tal caso, l'utilizzo !consentirà di evitare errori nel caso in cui un modulo precedentemente concatenato abbia perso il punto e virgola, e tuttavia ciò darà la flessibilità di metterli in qualsiasi ordine senza preoccupazioni.

!function abc(){}();
!function bca(){}();

Funzionerà come

!function abc(){}();
(function bca(){})();

ma salva un personaggio e l'aspetto arbitrario è migliore.

E tra l'altro qualsiasi +, -, ~, voidgli operatori hanno lo stesso effetto, in termini di invocare la funzione, di sicuro se si deve usare qualcosa di tornare da quella funzione si comporterebbero in modo diverso.

abcval = !function abc(){return true;}() // abcval equals false
bcaval = +function bca(){return true;}() // bcaval equals 1
zyxval = -function zyx(){return true;}() // zyxval equals -1
xyzval = ~function xyz(){return true;}() // your guess?

ma se si utilizzano i modelli IIFE per la separazione del codice di un file in un modulo e si utilizza lo strumento concat per l'ottimizzazione (che rende una riga un lavoro per file), quindi costruzione

!function abc(/*no returns*/) {}()
+function bca() {/*no returns*/}()

Farà l'esecuzione del codice in modo sicuro, come nel primo esempio di codice.

Questo genererà un errore perché JavaScript ASI non sarà in grado di fare il suo lavoro.

!function abc(/*no returns*/) {}()
(function bca() {/*no returns*/})()

Una nota per quanto riguarda gli operatori unari, avrebbero fatto un lavoro simile, ma solo nel caso in cui non fossero stati usati nel primo modulo. Quindi non sono così sicuri se non si ha il controllo totale sull'ordine di concatenazione.

Questo funziona:

!function abc(/*no returns*/) {}()
^function bca() {/*no returns*/}()

Questo non:

^function abc(/*no returns*/) {}()
!function bca() {/*no returns*/}()

3
In realtà, quegli altri simboli non hanno lo stesso effetto. Sì, ti consentono di chiamare una funzione come descritto, ma non sono identici. Considerare: var foo =! Function (bar) {console.debug (bar); } ( "bat"); Non importa quale dei tuoi simboli metti davanti, ottieni "bat" nella tua console. Ora aggiungi console.debug ("pippo:", pippo); - ottieni risultati molto diversi in base al simbolo che usi. ! forza un valore di ritorno che non è sempre desiderabile. Preferisco la sintassi ({}) () per chiarezza e accuratezza.
Carnix,

29

Restituisce se l'istruzione può essere valutata come falsa. per esempio:

!false      // true
!true       // false
!isValid()  // is not valid

Puoi usarlo due volte per forzare un valore in booleano:

!!1    // true
!!0    // false

Quindi, per rispondere più direttamente alla tua domanda:

var myVar = !function(){ return false; }();  // myVar contains true

Modifica: ha l'effetto collaterale di cambiare la dichiarazione di funzione in un'espressione di funzione. Ad esempio il seguente codice non è valido perché viene interpretato come una dichiarazione di funzione in cui manca l' identificatore richiesto (o il nome della funzione ):

function () { return false; }();  // syntax error

6
Per motivi di chiarezza per i lettori che potrebbero voler utilizzare un compito con una funzione immediatamente invocata, il codice di esempio var myVar = !function(){ return false; }()potrebbe omettere il !simile var myVar = function(){ return false; }()e la funzione verrà eseguita correttamente e il valore restituito rimarrà intatto.
Mark Fox,

1
Per essere chiari, puoi usarlo una volta per forzare in booleano, perché è un logico non operatore. ! 0 = vero e! 1 = falso. Ai fini della minificazione JavaScript, si desidera sostituire truecon !0e falsecon !1. Salva 2 o 3 caratteri.
Triynko,

9

È solo per salvare un byte di dati quando eseguiamo la minificazione javascript.

considera la seguente funzione anonima

function (){}

Per rendere quanto sopra come funzione auto-invocante, cambieremo generalmente il codice sopra come

(function (){}())

Ora abbiamo aggiunto due caratteri aggiuntivi (,)oltre a aggiungere ()alla fine della funzione che è necessario per chiamare la funzione. Nel processo di minificazione ci concentriamo generalmente per ridurre le dimensioni del file. Quindi possiamo anche scrivere la funzione sopra come

!function (){}()

Entrambi sono comunque funzioni auto invocanti e salviamo anche un byte. Invece di 2 personaggi (,)abbiamo usato solo un personaggio!


1
Questo è utile perché spesso lo vedrai in js minimizzato
Per il nome

5

! è un operatore NOT logico , è un operatore booleano che invertirà qualcosa al suo contrario.

Sebbene sia possibile ignorare le parentesi della funzione invocata utilizzando BANG (!) Prima della funzione, invertirà comunque il ritorno, che potrebbe non essere quello desiderato. Come nel caso di un IEFE, ritornerebbe indefinito , che quando invertito diventa il vero booleano.

Utilizzare invece la parentesi di chiusura e il BANG ( ! ) Se necessario.

// I'm going to leave the closing () in all examples as invoking the function with just ! and () takes away from what's happening.

(function(){ return false; }());
=> false

!(function(){ return false; }());
=> true

!!(function(){ return false; }());
=> false

!!!(function(){ return false; }());
=> true

Altri operatori che funzionano ...

+(function(){ return false; }());
=> 0

-(function(){ return false; }());
=> -0

~(function(){ return false; }());
=> -1

Operatori combinati ...

+!(function(){ return false; }());
=> 1

-!(function(){ return false; }());
=> -1

!+(function(){ return false; }());
=> true

!-(function(){ return false; }());
=> true

~!(function(){ return false; }());
=> -2

~!!(function(){ return false; }());
=> -1

+~(function(){ return false; }());
+> -1

5

Il punto esclamativo rende qualsiasi funzione restituisce sempre un valore booleano.
Il valore finale è la negazione del valore restituito dalla funzione.

!function bool() { return false; }() // true
!function bool() { return true; }() // false

Omettere !negli esempi sopra sarebbe un SyntaxError .

function bool() { return true; }() // SyntaxError

Tuttavia, un modo migliore per raggiungere questo obiettivo sarebbe:

(function bool() { return true; })() // true

Questo non è corretto !cambia il modo in cui il runtime analizza la funzione. Fa in modo che il runtime tratti la funzione come espressione di funzione (e non come una dichiarazione). Lo fa per consentire allo sviluppatore di invocare immediatamente la funzione utilizzando la ()sintassi. !si applicherà anche (cioè negazione) al risultato dell'invocazione dell'espressione della funzione.
Ben Aston,

3

È un altro modo di scrivere IIFE (espressione di funzione immediatamente invocata).

L'altro modo di scrivere -

(function( args ) {})()

uguale a

!function ( args ) {}();

Bene, non è esattamente lo stesso; il 2 ° modulo annulla il risultato della chiamata di funzione (e quindi lo butta via, perché non c'è assegnazione di valore). Preferirei rigorosamente la (function (args) {...})()sintassi più esplicita e lascerei quel !functionmodulo agli strumenti di minimizzazione e offuscamento.
Tobias,

-1

! annullerà (al contrario) qualunque cosa tu ti aspetti di conseguenza, cioè se hai

var boy = true;
undefined
boy
true
!boy
false

quando chiami boy, il tuo risultato sarà true, ma nel momento in cui aggiungi il !quando chiami boy, cioè il !boytuo risultato sarà false. Che in altre parole vuoi dire NotBoy , ma questa volta è fondamentalmente un risultato booleano, o trueo false.

È la stessa cosa che accade !function () {}();all'espressione, l'esecuzione solo function () {}();ti segnalerà un errore, ma aggiungendolo !proprio davanti alla tua function () {}();espressione, lo rende l'opposto di quello function () {}();che dovrebbe restituirti true. Di seguito è riportato un esempio:

function () {}();
SyntaxError: function statement requires a name
!function () {}();
true
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.