Perché è necessario richiamare una funzione anonima sulla stessa linea?


374

Stavo leggendo alcuni post sulle chiusure e l'ho visto ovunque, ma non c'è una spiegazione chiara su come funzioni - ogni volta che mi è stato appena detto di usarlo ...:

// Create a new anonymous function, to use as a wrapper
(function(){
    // The variable that would, normally, be global
    var msg = "Thanks for visiting!";

    // Binding a new function to a global object
    window.onunload = function(){
        // Which uses the 'hidden' variable
        alert( msg );
    };
// Close off the anonymous function and execute it
})();

Ok, vedo che creeremo una nuova funzione anonima e la eseguiremo. Quindi dopo questo semplice codice dovrebbe funzionare (e funziona):

(function (msg){alert(msg)})('SO');

La mia domanda è: che tipo di magia accade qui? Ho pensato che quando ho scritto:

(function (msg){alert(msg)})

quindi una nuova funzione senza nome verrebbe creata come la funzione "" (msg) ...

ma allora perché non funziona?

(function (msg){alert(msg)});
('SO');

Perché deve essere nella stessa linea?

Potresti indicarmi alcuni post o darmi una spiegazione?


2
In altre lingue, questi sono chiamati puntatori a funzione o delegati, se si desidera esaminare le strutture di livello inferiore coinvolte.
Chris Moschini,

17
Hai un ; nella prima riga
Oliver Ni,

Ora che sai come funziona ... Non usarlo. Dovremmo smettere di scrivere funzioni anonime . Con solo pochi altri caratteri possiamo dare un nome reale alle nostre funzioni e rendere il debug del codice Javascript molto più semplice!
Stijn de Witt,

1
La linea (function (msg){alert(msg)})('SO');funziona completamente da sola. Non ha nulla a che fare con l'altra funzione anonima che hai pubblicato prima. Queste sono due funzioni anonime completamente separate. Devi invocare immediatamente una funzione anonima perché non ha nome e non può essere referenziata in seguito.
Polpo,

Risposte:


380

Rilascia il punto e virgola dopo la definizione della funzione.

(function (msg){alert(msg)})
('SO');

Sopra dovrebbe funzionare.

Pagina DEMO: https://jsfiddle.net/e7ooeq6m/

Ho discusso di questo tipo di modello in questo post:

jQuery e $ domande

MODIFICARE:

Se si osserva la specifica dello script ECMA , è possibile definire una funzione in 3 modi. (Pagina 98, Sezione 13 Definizione delle funzioni)

1. Utilizzo del costruttore di funzioni

var sum = new Function('a','b', 'return a + b;');
alert(sum(10, 20)); //alerts 30

2. Utilizzo della dichiarazione di funzione.

function sum(a, b)
{
    return a + b;
}

alert(sum(10, 10)); //Alerts 20;

3. Espressione di funzione

var sum = function(a, b) { return a + b; }

alert(sum(5, 5)); // alerts 10

Quindi potresti chiedere, qual è la differenza tra dichiarazione ed espressione?

Dalle specifiche dello script ECMA:

FunctionDeclaration: function Identifier (FormalParameterListopt) {FunctionBody}

FunctionExpression: function Identifieropt (FormalParameterListopt) {FunctionBody}

Se noti, "identificatore" è facoltativo per l'espressione della funzione. E quando non dai un identificatore, crei una funzione anonima. Ciò non significa che non è possibile specificare un identificatore.

Ciò significa che seguire è valido.

var sum = function mySum(a, b) { return a + b; }

È importante notare che è possibile utilizzare "mySum" solo all'interno del corpo della funzione mySum, non all'esterno. Vedi il seguente esempio:

var test1 = function test2() { alert(typeof test2); }

alert(typeof(test2)); //alerts 'undefined', surprise! 

test1(); //alerts 'function' because test2 is a function.

Dimostrazione dal vivo

Confronta questo con

 function test1() { alert(typeof test1) };

 alert(typeof test1); //alerts 'function'

 test1(); //alerts 'function'

Grazie a questa conoscenza, proviamo ad analizzare il tuo codice.

Quando hai un codice simile,

    function(msg) { alert(msg); }

Hai creato un'espressione di funzione. E puoi eseguire questa espressione di funzione avvolgendola tra parentesi.

    (function(msg) { alert(msg); })('SO'); //alerts SO.

1
Sì, ma perché? Perché deve essere in linea? Non importa quanti spazi bianchi userò.
Palig,

9
Come ho scritto, il punto e virgola ha terminato la definizione della funzione anonima. Poiché non ha un nome (è anonimo duh!), Non potrai più chiamarlo. Se non si inserisce il punto e virgola, la funzione potrebbe comunque essere eseguita.
SolutionYogi,

Ho pensato che l'inserimento automatico del punto e virgola avrebbe inserito un punto e virgola in questo caso, ma non è così. Quindi hai ragione.
Nosredna,

1
Nosredna, JS si comporta poco arbitrariamente quando si tratta di aggiungere punti e virgola. Leggi questo articolo dettagliato: blog.boyet.com/blog/javascriptlessons/…
SolutionYogi

Sì, vedo che (funzione (msg) {alert (msg)}) ('SO'); lavori. Stavo solo chiedendo perché funziona. Dove viene specificato o che tipo di funzionalità JS è questa. Quindi una volta che ho appena chiamato: (function (msg) {alert (msg)}) cosa accadrà con la funzione? Sarà GC?
Palig,

129

Si chiama funzione auto-invocata.

Quello che stai facendo quando chiami (function(){})è la restituzione di un oggetto funzione. Quando lo aggiungi (), viene invocato e tutto il corpo viene eseguito. Il ;indica la fine dell'istruzione, che è il motivo per cui il 2 ° invocazione fallisce.


Ah ok capisco, quindi è solo una sintassi speciale di JS, giusto? Ti piace di più questa spiegazione! Semplice e breve :)
Palig

Penso che sia errato affermare che il corpo sarà "valutato". Esegue esattamente come qualsiasi altra funzione. Poiché è anonimo, è possibile salvare il riferimento da qualche parte O eseguirlo immediatamente.
SolutionYogi,

16
Personalmente, non mi piace nemmeno il termine "funzione auto-invocante". Non è quella funzione che sta invocando se stessa. Il programmatore ha scritto quelle parentesi per invocarla.
SolutionYogi,

Non è "sintassi speciale" più di ogni altra cosa è speciale. In realtà, il modulo "nome funzione (args) {BLOCK}" è molto più "speciale". In realtà è zucchero inutile; questo, tuttavia, è ciò che realmente fa accadere le cose.
jrockway,

2
bel link all'articolo. Nota perché qualcuno dovrebbe usare questo citato: "Nel tentativo di proteggere l'oggetto globale, tutte le applicazioni JavaScript dovrebbero essere scritte all'interno di una funzione auto-invocante. Ciò creerà un ambito di applicazione in cui le variabili possono essere create senza il timore che si scontrino con altre applicazioni ". E ha anche osservato "Una volta terminata la funzione, le variabili vengono scartate e l'oggetto globale rimane invariato".
Sì,

94

Una cosa che ho trovato confuso è che "()" sono operatori di raggruppamento.

Ecco la tua funzione dichiarata di base.

Ex. 1:

var message = 'SO';

function foo(msg) {
    alert(msg);
}

foo(message);

Le funzioni sono oggetti e possono essere raggruppate. Quindi lanciamo parentesi attorno alla funzione.

Ex. 2:

var message = 'SO';

function foo(msg) {  //declares foo
    alert(msg);
}

(foo)(message);     // calls foo

Ora invece di dichiarare e chiamare subito la stessa funzione, possiamo usare la sostituzione di base per dichiararla come la chiamiamo.

Ex. 3.

var message = 'SO';

(function foo(msg) {
    alert(msg);
})(message);          // declares & calls foo

Alla fine, non abbiamo bisogno di quell'altro povero perché non stiamo usando il nome per chiamarlo! Le funzioni possono essere anonime.

Ex. 4.

var message = 'SO';

(function (msg) {   // remove unnecessary reference to foo
    alert(msg);
})(message);

Per rispondere alla tua domanda, fai riferimento all'esempio 2. La tua prima riga dichiara alcune funzioni senza nome e le raggruppa, ma non la chiama. La seconda riga raggruppa una stringa. Entrambi non fanno nulla. (Il primo esempio di Vincent.)

(function (msg){alert(msg)});  
('SO');                       // nothing.

(foo); 
(msg); //Still nothing.

Ma

(foo)
(msg); //works

6
Grazie. I tuoi esempi sono stati abbastanza chiari. Non sapevo che le parentesi in JavaScript potessero cambiare il significato del codice in questo modo. Vengo da un background Java, quindi imparo qualcosa di nuovo (e spesso inaspettato) su JavaScript quasi ogni giorno che lo uso.
hotshot309

5
Grazie per averlo fatto passo dopo passo, questo è molto meglio di qualsiasi altra spiegazione che ho visto. +1
Wk_of_Angmar il

2
Importante momento AHA qui e grazie per l'illustrazione con sostituzione. +100
FredTheWebGuy

1
Una delle migliori spiegazioni che ho letto su funzioni anonime. Molte grazie!
Teknotica,

23

Una funzione anonima non è una funzione con il nome "". È semplicemente una funzione senza nome.

Come qualsiasi altro valore in JavaScript, una funzione non ha bisogno di un nome per essere creata. Anche se è molto più utile associarlo effettivamente a un nome proprio come qualsiasi altro valore.

Ma come qualsiasi altro valore, a volte vuoi usarlo senza vincolarlo a un nome. Questo è il modello auto-invocante.

Ecco una funzione e un numero, non associati, non fanno nulla e non possono mai essere usati:

function(){ alert("plop"); }
2;

Quindi dobbiamo memorizzarli in una variabile per poterli usare, proprio come qualsiasi altro valore:

var f = function(){ alert("plop"); }
var n = 2;

Puoi anche usare lo zucchero sintatico per associare la funzione a una variabile:

function f(){ alert("plop"); }
var n = 2;

Ma se la loro denominazione non è richiesta e porterebbe a maggiore confusione e minore leggibilità, è possibile utilizzarle immediatamente.

(function(){ alert("plop"); })(); // will display "plop"
alert(2 + 3); // will display 5

Qui, la mia funzione e i miei numeri non sono associati a una variabile, ma possono ancora essere utilizzati.

Detto questo, sembra che la funzione auto-invocante non abbia alcun valore reale. Ma devi tenere presente che il delimitatore di ambito JavaScript è la funzione e non il blocco ({}).

Quindi una funzione auto-invocante ha in realtà lo stesso significato di un blocco C ++, C # o Java. Ciò significa che la variabile creata all'interno non "colerà" al di fuori dell'ambito. Questo è molto utile in JavaScript per non inquinare l'ambito globale.


Bel post. Cosa succederà poi con 'function () {alert ("plop"); } "quando l'ho eseguito? Sarà GC?
Palig,

2
La funzione () {alert ("plop"); } l'istruzione alloca solo la funzione ma non la esegue né la lega a una variabile. Poiché la funzione creata non è associata a nessuna variabile, verrà rapidamente GCed.
Vincent Robert,

Questo thread SO va oltre lo scopo di ciò di cui stiamo parlando qui, ma spiega i modi per separare gli spazi dei nomi JavaScript e include esempi che utilizzano funzioni auto-invocanti.
hotshot309

19

È proprio come funziona JavaScript. È possibile dichiarare una funzione denominata:

function foo(msg){
   alert(msg);
}

E chiamalo:

foo("Hi!");

In alternativa, puoi dichiarare una funzione anonima:

var foo = function (msg) {
    alert(msg);
}

E chiamalo così:

foo("Hi!");

In alternativa, non puoi mai associare la funzione a un nome:

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

Le funzioni possono anche restituire funzioni:

function make_foo() {
    return function(msg){ alert(msg) };
}

(make_foo())("Hi!");

Non vale nulla che qualsiasi variabile definita con "var" nel corpo di make_fooverrà chiusa da ciascuna funzione restituita da make_foo. Questa è una chiusura e significa che qualsiasi modifica apportata al valore da una funzione sarà visibile da un'altra.

Questo ti consente di incapsulare informazioni, se lo desideri:

function make_greeter(msg){
    return function() { alert(msg) };
}

var hello = make_greeter("Hello!");

hello();

È come funziona quasi tutti i linguaggi di programmazione tranne Java.


8

Il codice che mostri,

(function (msg){alert(msg)});
('SO');

consistono in due affermazioni. La prima è un'espressione che produce un oggetto funzione (che verrà quindi raccolto in modo errato perché non viene salvato). La seconda è un'espressione che produce una stringa. Per applicare la funzione alla stringa, è necessario passare la stringa come argomento alla funzione quando viene creata (come mostrato anche in precedenza) oppure è necessario archiviare effettivamente la funzione in una variabile, in modo da poter applicalo in un secondo momento, a tuo piacimento. Così:

var f = (function (msg){alert(msg)});
f('SO');

Si noti che memorizzando una funzione anonima (una funzione lambda) in una variabile, si sta effettivamente dando un nome. Quindi puoi anche definire una funzione regolare:

function f(msg) {alert(msg)};
f('SO');

7

In sintesi dei commenti precedenti:

function() {
  alert("hello");
}();

quando non assegnato a una variabile, genera un errore di sintassi. Il codice viene analizzato come un'istruzione di funzione (o definizione), che rende sintatticamente errate le parentesi di chiusura. L'aggiunta di parentesi attorno alla parte di funzione dice all'interprete (e al programmatore) che questa è un'espressione di funzione (o invocazione), come in

(function() {
  alert("hello");
})();

Questa è una funzione auto-invocante, nel senso che viene creata in modo anonimo e viene eseguita immediatamente perché l'invocazione avviene nella stessa riga in cui viene dichiarata. Questa funzione di auto-invocazione è indicato con la sintassi familiare per chiamare una funzione senza argomenti, oltre parentesi aggiunto attorno al nome della funzione: (myFunction)();.

Esiste una buona sintassi della funzione JavaScript della discussione SO .


3

Questa risposta non è strettamente correlata alla domanda, ma potresti essere interessato a scoprire che questo tipo di funzionalità di sintassi non è specifica delle funzioni. Ad esempio, possiamo sempre fare qualcosa del genere:

alert(
    {foo: "I am foo", bar: "I am bar"}.foo
); // alerts "I am foo"

Relativo alle funzioni. Dato che sono oggetti, che ereditano da Function.prototype, possiamo fare cose come:

Function.prototype.foo = function () {
    return function () {
        alert("foo");
    };
};

var bar = (function () {}).foo();

bar(); // alerts foo

E sai, non dobbiamo nemmeno circondare le funzioni tra parentesi per eseguirle. Ad ogni modo, purché proviamo ad assegnare il risultato a una variabile.

var x = function () {} (); // this function is executed but does nothing

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

Un'altra cosa che puoi fare con le funzioni, non appena le dichiari, è invocare l' newoperatore su di esse e ottenere un oggetto. Sono equivalenti:

var obj = new function () {
    this.foo = "bar";
};

var obj = {
    foo : "bar"
};

3

Esiste un'altra proprietà della funzione JavaScript. Se si desidera chiamare la stessa funzione anonima in modo ricorsivo.

(function forInternalOnly(){

  //you can use forInternalOnly to call this anonymous function
  /// forInternalOnly can be used inside function only, like
  var result = forInternalOnly();
})();

//this will not work
forInternalOnly();// no such a method exist

2
+1 Aggiunto un piccolo campione in modo che sia più chiaro :-) La prima volta che l'ho letto ho dovuto rileggere 4 volte.
xanatos,

3

La mia comprensione della domanda del richiedente è tale che:

Come funziona questa magia:

(function(){}) ('input')   // Used in his example

Potrei sbagliarmi. Tuttavia, la pratica abituale con cui le persone hanno familiarità è:

(function(){}('input') )

Il motivo è che JavaScript tra parentesi ()non può contenere istruzioni e quando il parser incontra la parola chiave funzione, sa analizzarla come espressione di funzione e non come dichiarazione di funzione.

Fonte: post sul blog Immediately-Invoked Function Expression (IIFE)


3

esempi senza parentesi:

void function (msg) { alert(msg); }
('SO');

(questo è l'unico vero uso del vuoto, afaik)

o

var a = function (msg) { alert(msg); }
('SO');

o

!function (msg) { alert(msg); }
('SO');

funziona pure. la voidcausa l'Un'espressione da valutare, nonché l'assegnazione e la scoppio. l'ultima opera con ~, +, -, delete, typeof, alcuni degli operatori unari ( voidè uno pure). non funzionare sono utili ++,-- causa del requisito di una variabile.

l'interruzione di linea non è necessaria.


@Bergi su ie11 deletefunziona. anche con 'use strict';. funziona anche questo:delete (3 + 4);
Nina Scholz,

Ooops, errore mio. " 2) Se Type (ref) non è Reference, restituisce true. " Genera solo errori per riferimenti effettivi irrisolvibili.
Bergi,

1

È una funzione anonima autoeseguente. La prima serie di parentesi contiene le espressioni da eseguire e la seconda serie di parentesi esegue quelle espressioni.

(function () {
    return ( 10 + 20 );
})();

Peter Michaux discute la differenza in Una coppia importante di parentesi .

È un costrutto utile quando si tenta di nascondere le variabili dallo spazio dei nomi padre. Tutto il codice all'interno della funzione è contenuto nell'ambito privato della funzione, il che significa che non è possibile accedervi dall'esterno della funzione, rendendolo veramente privato.

Vedere:

  1. Chiusura (informatica)
  2. Spaziatura dei nomi JavaScript
  3. Coppia importante di parentesi Javascript

0

Un altro punto di vista

Innanzitutto, puoi dichiarare una funzione anonima:

var foo = function(msg){
 alert(msg);
}

Quindi lo chiami:

foo ('Few');

Perché foo = function (msg) {alert (msg);} così puoi sostituire foo come:

function(msg){
 alert(msg);
} ('Few');

Ma dovresti avvolgere l'intera funzione anonima all'interno di una coppia di parentesi graffe per evitare errori di sintassi della dichiarazione di funzione durante l'analisi. Poi abbiamo,

(function(msg){
 alert(msg);
}) ('Few');

In questo modo, è facile da capire per me.


0

Quando hai fatto:

(function (msg){alert(msg)});
('SO');

Hai terminato la funzione prima a ('SO')causa del punto e virgola. Se scrivi solo:

(function (msg){alert(msg)})
('SO');

Funzionerà.

Esempio di lavoro: http://jsfiddle.net/oliverni/dbVjg/


0

Il semplice motivo per cui non funziona non è dovuto ;all'indicazione della fine della funzione anonima. È perché senza la ()fine di una chiamata di funzione, non è una chiamata di funzione. Questo è,

function help() {return true;}

Se chiamate result = help();questa è una chiamata a una funzione e tornerà vera.

Se chiami result = help;questa non è una chiamata. È un compito in cui la guida viene trattata come i dati da assegnare al risultato.

Quello che hai fatto è stato dichiarare / creare un'istanza di una funzione anonima aggiungendo il punto e virgola,

(function (msg) { /* Code here */ });

e poi ho provato a chiamarlo in un'altra istruzione usando solo parentesi ... Ovviamente perché la funzione non ha un nome, ma questo non funzionerà:

('SO');

L'interprete vede le parentesi sulla seconda riga come una nuova istruzione / istruzione, e quindi non funziona, anche se l'hai fatto in questo modo:

(function (msg){/*code here*/});('SO');

Non funziona ancora, ma funziona quando si rimuove il punto e virgola perché l'interprete ignora gli spazi bianchi e i carrelli e vede il codice completo come un'istruzione.

(function (msg){/*code here*/})        // This space is ignored by the interpreter
('SO');

Conclusione: una chiamata di funzione non è una chiamata di funzione senza l' ()end, a meno che in condizioni specifiche come essere invocata da un'altra funzione, ovvero onload = 'help' eseguirà la funzione di aiuto anche se le parentesi non sono state incluse. Credo che anche setTimeout e setInterval consentano anche questo tipo di chiamata di funzione, e credo anche che l'interprete aggiunga comunque le parentesi dietro le quinte che ci riportano a "una chiamata di funzione non è una chiamata di funzione senza le parentesi".


Non capisco perché questo abbia ricevuto così tanti voti negativi. Penso che sia una risposta accettabile? : /
Daniel Cheung,

0
(function (msg){alert(msg)})
('SO');

Questo è un metodo comune per utilizzare una funzione anonima come chiusura utilizzata da molti framework JavaScript.

Questa funzione chiamata è automaticamente quando viene compilato il codice.

Se si posiziona ;sulla prima riga, il compilatore lo tratta come due righe diverse. Quindi non puoi ottenere gli stessi risultati di cui sopra.

Questo può anche essere scritto come:

(function (msg){alert(msg)}('SO'));

Per maggiori dettagli, guarda in JavaScript / Funzioni anonime .


Per quanto ne so, JavaScript non "si compila"
Daniel Cheung,

0
  1. Le funzioni anonime sono funzioni dichiarate dinamicamente in fase di esecuzione. Sono chiamate funzioni anonime perché non hanno un nome allo stesso modo delle normali funzioni.

    Le funzioni anonime vengono dichiarate utilizzando l'operatore della funzione anziché la dichiarazione della funzione. È possibile utilizzare l'operatore della funzione per creare una nuova funzione ovunque sia valida per inserire un'espressione. Ad esempio, è possibile dichiarare una nuova funzione come parametro per una chiamata di funzione o assegnare una proprietà di un altro oggetto.

    Ecco un tipico esempio di una funzione con nome:

    funzione flyToTheMoon () {alert ("Zoom! Zoom! Zoom!"); } flyToTheMoon (); Ecco lo stesso esempio creato come una funzione anonima:

    var flyToTheMoon = function () {alert ("Zoom! Zoom! Zoom!"); } flyToTheMoon ();

    Per i dettagli, leggi qui:

    http://helephant.com/2008/08/23/javascript-anonymous-functions/


0

L'IIFE suddivide semplicemente la funzione in compartimenti e nasconde la msgvariabile in modo da non "inquinare" lo spazio dei nomi globale. In realtà, mantienilo semplice e fai come di seguito, a meno che tu non stia costruendo un sito Web da un miliardo di dollari.

var msg = "later dude";
window.onunload = function(msg){
  alert( msg );
};

È possibile inserire nello spazio dei nomi la msgproprietà usando un modello di modulo rivelatore come:

var myScript = (function() {
    var pub = {};
    //myscript.msg
    pub.msg = "later dude";
    window.onunload = function(msg) {
        alert(msg);
    };
    //API
    return pub;
}());

-1

Le funzioni anonime sono pensate per essere un affare one-shot in cui definisci una funzione al volo in modo che generi un output da te da un input che stai fornendo. Solo che non hai fornito l'input. Invece, hai scritto qualcosa sulla seconda riga ('SO'); - un'istruzione indipendente che non ha nulla a che fare con la funzione. Cosa ti aspettavi? :)


Non corretto al 100%. Questa è una funzione anonima pure ed è pensato per essere riutilizzato: var foo = function() {};. Tutto il resto va bene però.
Felix Kling,
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.