Qual è lo scopo di una funzione di auto-esecuzione in JavaScript?


427

In javascript, quando vorresti usare questo:

(function(){
    //Bunch of code...
})();

oltre questo:

//Bunch of code...

3
Dai anche un'occhiata a una spiegazione ( tecnica ) e qui . Per la sintassi, vedi perché le parentesi sono necessarie e dove dovrebbero andare .
Bergi,


Perché ha le ultime due parentesi, appena prima del punto e virgola?
johnny,

3
@johnny la parte prima di quelle ultime due parentesi dichiara una funzione (anonima). Queste due parentesi chiamano la funzione.
Ej.

7
"Espressione di funzione immediatamente richiamata" o IIFE è un nome migliore per questo.
Flimm,

Risposte:


404

Si tratta di scoping variabile. Le variabili dichiarate nella funzione di auto-esecuzione sono, per impostazione predefinita, disponibili solo per il codice all'interno della funzione di auto-esecuzione. Ciò consente di scrivere il codice senza preoccuparsi di come le variabili vengono denominate in altri blocchi di codice JavaScript.

Ad esempio, come menzionato in un commento di Alexander :

(function() {
  var foo = 3;
  console.log(foo);
})();

console.log(foo);

Questo prima registrerà 3e poi lancerà un errore sul successivo console.logperché foonon è definito.


7
E anche a beneficio di molte persone là fuori, tra cui un sacco di ingegneri Netflix: È SOLO UNA FUNZIONE. Non è di per sé rappresentativo di una chiusura. A volte gli auto-invocatori vengono utilizzati insieme a scenari rilevanti per la chiusura per fare cose ordinate, ma se non vedi qualcosa che si aggrappa a un riferimento che verrebbe spazzato via e portato in un linguaggio senza chiusura, non ha nulla da fottutamente da fare con le CHIUSURE.
Erik Reppen,

2
Quindi questo significa che viene usato principalmente con la chiusura?
Pir Abdul,

@AlexanderBird, che non va ... se lo fai senza var, in questo modo: ...function(){ foo=3;}? Impostarebbe una variabile globale, giusto?
T.Todua,

2
@AlexanderBird, ma ciò accade già nelle variabili locali all'interno delle funzioni: function(){ var foo = 3; alert(foo); }; alert(foo);quindi ancora non capisco
João Pimentel Ferreira,

Ah, ho capito, ottieni tutte queste 3 caratteristiche contemporaneamente: 1) esegui la funzione dichiarandola solo; 2) Come qualsiasi funzione, l'ambito delle variabili è solo locale; e 3) La funzione potrebbe essere anonima, non inquinando l'ambito principale
João Pimentel Ferreira,

94

Semplicistico. Quindi aspetto molto normale, è quasi confortante:

var userName = "Sean";

console.log(name());

function name() {
  return userName;
}

Tuttavia, cosa succede se includo una libreria JavaScript davvero utile nella mia pagina che traduce i caratteri avanzati nelle loro rappresentazioni di livello base?

Aspetta cosa?

Voglio dire, se qualcuno digita un personaggio con una sorta di accento, ma voglio solo caratteri "inglesi" AZ nel mio programma? Bene ... i caratteri "é" spagnoli e "é" francesi possono essere tradotti in caratteri base di "n" ed "e".

Quindi una persona gentile ha scritto un convertitore di caratteri completo che posso includere nel mio sito ... Lo includo.

Un problema: ha una funzione chiamata 'nome' come la mia funzione.

Questo è ciò che si chiama collisione. Abbiamo due funzioni dichiarate nello stesso ambito con lo stesso nome. Vogliamo evitare questo.

Quindi dobbiamo in qualche modo estendere il nostro codice.

L'unico modo per ottenere il codice scope in javascript è racchiuderlo in una funzione:

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

Questo potrebbe risolvere il nostro problema. Ora tutto è racchiuso e vi si può accedere solo dalle nostre parentesi graffe di apertura e chiusura.

Abbiamo una funzione in una funzione ... che è strana da guardare, ma totalmente legale.

Un solo problema. Il nostro codice non funziona. La nostra variabile userName non viene mai ripetuta nella console!

Possiamo risolvere questo problema aggiungendo una chiamata alla nostra funzione dopo il nostro blocco di codice esistente ...

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

main();

O prima!

main();

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

Una preoccupazione secondaria: quali sono le possibilità che il nome 'main' non sia ancora stato usato? ... molto, molto magro.

Abbiamo bisogno di PIÙ scoping. E un modo per eseguire automaticamente la nostra funzione main ().

Ora arriviamo alle funzioni di auto-esecuzione (o auto-esecuzione, auto-corsa, qualunque cosa).

((){})();

La sintassi è imbarazzante come peccato. Tuttavia funziona.

Quando si racchiude una definizione di funzione tra parentesi e si include un elenco di parametri (un altro set o parentesi!) Si comporta come una chiamata di funzione .

Vediamo quindi di nuovo il nostro codice, con qualche sintassi auto-eseguita:

(function main() {
  var userName = "Sean";

    console.log(name());

    function name() {
      return userName;
    }
  }
)();

Quindi, nella maggior parte dei tutorial che leggi, ora sarai bombardato dal termine "auto-esecuzione anonima" o qualcosa di simile.

Dopo molti anni di sviluppo professionale, ti consiglio vivamente di nominare ogni funzione che scrivi per scopi di debug.

Quando qualcosa va storto (e lo farà), controllerai la backtrace nel tuo browser. È sempre più semplice restringere i problemi del codice quando le voci nella traccia dello stack hanno nomi!

Molto prolisso e spero che sia d'aiuto!


2
Grazie :) Stavo cercando in tutto il mondo cercando di capire i vantaggi di IIFE in relazione alle normali funzioni in termini di privacy variabile e la tua risposta è semplicemente la migliore. Tutti dicono che uno dei migliori vantaggi è che le variabili e le funzioni all'interno di IIFE sono "finalmente" private quando una normale funzione ti dà esattamente la stessa cosa. Alla fine penso di averlo capito attraverso il tuo processo di spiegazione. Dopo tutto, IIFE è solo una funzione, ma ora capisco perché usarla. Grazie ancora!
viery365,

Grazie per aver dedicato del tempo a spiegarlo così bene.
MSC

Bella risposta. Ho una domanda sul tuo ultimo punto, però: quando consigli di dare un nome a tutte le funzioni, stai dicendo che c'è un modo per farlo con funzioni auto-eseguibili o suggerendo che tutti chiamino la funzione e la chiamino? EDIT Oh, capisco. Questo è già chiamato. Duh. Potrebbe voler sottolineare che stai giustificando il tuo utilizzo di una funzione di auto-esecuzione denominata.
FireSBurnsmuP

Bene amico mio, questa è LA risposta che stavo cercando:)
João Pimentel Ferreira il

Questa è la risposta che stavo cercando, non quella che è stata accettata. Ho ragione nel dire che non si tratta della collisione di nomi di variabili , ma della collisione di nomi di funzioni ? Le variabili anche nelle normali funzioni non iife sono di ambito locale e non si scontrano con variabili globali, non è vero?
Kumar Manish,

32

L'auto-invocazione (nota anche come auto-invocazione) è quando una funzione viene eseguita immediatamente dopo la sua definizione. Questo è un modello di base e funge da base per molti altri modelli di sviluppo JavaScript.

Sono un grande fan :) perché:

  • Mantiene il codice al minimo
  • Impone la separazione del comportamento dalla presentazione
  • Fornisce una chiusura che impedisce conflitti di denominazione

Enorme - (Perché dovresti dire che è buono?)

  • Si tratta di definire ed eseguire una funzione tutto in una volta.
  • È possibile che quella funzione auto-eseguente restituisca un valore e passi la funzione come parametro a un'altra funzione.
  • È buono per l'incapsulamento.
  • È anche buono per l'ambito scoping.
  • Sì, puoi racchiudere tutti i tuoi file .js in una funzione di auto-esecuzione e puoi prevenire l'inquinamento dello spazio dei nomi globale. ;)

Più qui .


43
Punto 1. Come? Punto 2. Proviene da una migliore pratica completamente diversa. Punto 3. Quale funzione non funziona? 4,5,6,7. Rilevanza? 8. Beh, 1/8 non è male credo.
Erik Reppen,

2
Sette anni di ritardo ma, per il punto 1. non riduce affatto il codice, anzi aggiunge un minimo di due righe di codice nel creare la funzione.
YungGun

1
l'unico punto qui è "Fornisce una chiusura che impedisce conflitti di denominazione", ogni altro punto è una riformulazione di questo o falso. forse puoi semplificare la tua risposta?
pcarvalho,

19

Namespacing. Gli ambiti di JavaScript sono a livello di funzione.


7
i downvotes stanno ancora arrivando perché ho usato lo spazio dei nomi instad di scoping ; questa è una questione di definizione - vedi ad esempio Wikipedia : uno spazio dei nomi nell'informatica (a volte chiamato anche ambito di nomi), è un contenitore o un ambiente astratto creato per contenere un raggruppamento logico di identificatori o simboli univoci (ad esempio, nomi). e Un identificatore di spazio dei nomi può fornire un contesto (Scope in informatica) a un nome, e i termini sono talvolta usati in modo intercambiabile.
Christoph,

5
Gli ambiti a livello di funzione di Javascript forniscono lo spazio in cui vivono i nomi delle variabili, uno spazio dei nomi ; che sia anonimo non associato a un identificatore di spazio dei nomi è irrilevante ...
Christoph

12

Non posso credere che nessuna delle risposte menzioni i globuli impliciti.

Il (function(){})()costrutto non protegge dai globi impliciti, che per me è la preoccupazione maggiore, vedi http://yuiblog.com/blog/2006/06/01/global-domination/

Fondamentalmente il blocco funzione assicura che tutti i "parametri globali" dipendenti che hai definito siano confinati nel tuo programma, non ti protegge dalla definizione di globali impliciti. JSHint o simili possono fornire consigli su come difendersi da questo comportamento.

La var App = {}sintassi più concisa fornisce un livello di protezione simile e può essere racchiusa nel blocco funzione su pagine "pubbliche". (vedi Ember.js o SproutCore per esempi reali di librerie che usano questo costrutto)

Per quanto riguarda le privateproprietà, sono in qualche modo sopravvalutate a meno che non si stia creando una struttura pubblica o una biblioteca, ma se è necessario implementarle, Douglas Crockford ha delle buone idee.


8
La modalità rigorosa protegge dai globi impliciti. Che in combinazione con un auto-invocatore ti avrebbe coperto. Non ho mai capito l'hooplah sulle proprietà private. Dichiarare vari all'interno di un costruttore di funzioni. Fatto. Se il pensiero di dimenticare di usare la parola chiave "new" ti tiene sveglio la notte, scrivi una funzione di fabbrica. Fatto di nuovo.
Erik Reppen,

8

Ho letto tutte le risposte, qui manca qualcosa di molto importante , BACIO. Ci sono 2 motivi principali, perché ho bisogno di funzioni anonime auto-eseguite, o meglio detto " Espressione della funzione immediatamente invocata (IIFE) ":

  1. Migliore gestione dello spazio dei nomi (Evitare l'inquinamento dello spazio dei nomi -> Modulo JS)
  2. Chiusure (simulazione di membri della classe privata, come noto da OOP)

Il primo è stato spiegato molto bene. Per il secondo, si prega di studiare il seguente esempio:

var MyClosureObject = (function (){
  var MyName = 'Michael Jackson RIP';
  return {
    getMyName: function () { return MyName;},
    setMyName: function (name) { MyName = name}
  }
}());

Attenzione 1: Non stiamo assegnando una funzione a MyClosureObject, più ulteriormente il risultato dell'invocazione di quella funzione . Essere consapevoli ()dell'ultima riga.

Attenzione 2: ciò che devi sapere sulle funzioni in Javascript è che le funzioni interne hanno accesso ai parametri e alle variabili delle funzioni, all'interno delle quali sono definite.

Proviamo alcuni esperimenti:

Posso MyNameusare getMyNamee funziona:

 console.log(MyClosureObject.getMyName()); 
 // Michael Jackson RIP

Il seguente approccio ingegnoso non funzionerebbe:

console.log(MyClosureObject.MyName); 
// undefined

Ma posso impostare un altro nome e ottenere il risultato atteso:

MyClosureObject.setMyName('George Michael RIP');
console.log(MyClosureObject.getMyName()); 
// George Michael RIP

Modifica: nell'esempio sopra MyClosureObjectè progettato per essere utilizzato senza il newprefisso, quindi per convenzione non dovrebbe essere maiuscolo.


7

Esiste un parametro e il "Mazzo di codice" restituisce una funzione?

var a = function(x) { return function() { document.write(x); } }(something);

Chiusura. Il valore di somethingviene utilizzato dalla funzione assegnata a a. somethingpotrebbe avere un valore variabile (per loop) e ogni volta che a ha una nuova funzione.


+1; Preferisco un esplicito var x = something;nella funzione esterna rispetto xa un parametro, però: imo è più leggibile in questo modo ...
Christoph,

@Christoph: Se il valore di "qualcosa" cambia dopo che la funzione è stata creata, utilizzerà il nuovo valore e non quello al momento della sua creazione.
stesch

@stesch: da dove l'hai preso? Per quanto ne so, non è così; l'unico modo per ottenere riferimenti reali in JS è utilizzare l'oggetto argomenti, ma anche questo non funziona in tutti i browser
Christoph,

@Christoph: "JavaScript: The Good Parts", Douglas Crockford (O'Reilly)
stesch,

@stesch: non funziona nel modo in cui lo descrivi: il nuovo valore verrà utilizzato se elimini la variabile xe dipendi direttamente dall'ambito lessicale, ovvero document.write(something)...
Christoph,

6

Scope isolamento, forse. In modo che le variabili all'interno della dichiarazione della funzione non inquinino lo spazio dei nomi esterno.

Ovviamente, su metà delle implementazioni di JS là fuori, lo faranno comunque.


4
Quali implementazioni sarebbero quelle?
Matthew Crumley,

1
Qualsiasi implementazione non scritta in modalità rigorosa e contenente una dichiarazione var implicita che la rende globale.
Erik Reppen,

5

Ecco un solido esempio di come potrebbe essere utile una funzione anonima auto-invocante.

for( var i = 0; i < 10; i++ ) {
  setTimeout(function(){
    console.log(i)
  })
}

Produzione: 10, 10, 10, 10, 10...

for( var i = 0; i < 10; i++ ) {
  (function(num){
    setTimeout(function(){
      console.log(num)
    })
  })(i)
}

Produzione: 0, 1, 2, 3, 4...


puoi spiegare qualcosa in più su ciò che sta accadendo per la prima serie di codici
radio_head

Al letposto del varprimo caso andrà bene.
Vitaly Zdanevich,

3

Una differenza è che le variabili dichiarate nella funzione sono locali, quindi scompaiono quando si esce dalla funzione e non entrano in conflitto con altre variabili in altri codici.


1

Poiché le funzioni in Javascript sono oggetti di prima classe, definendolo in questo modo, definisce in modo efficace una "classe" molto simile a C ++ o C #.

Tale funzione può definire variabili locali e avere funzioni al suo interno. Le funzioni interne (efficacemente metodi di istanza) avranno accesso alle variabili locali (effettivamente variabili di istanza), ma saranno isolate dal resto dello script.


1

Funzione auto-invocata in javascript:

Un'espressione auto-invocante viene invocata (avviata) automaticamente, senza essere chiamata. Un'espressione auto-invocante viene invocata subito dopo la sua creazione. Questo è sostanzialmente usato per evitare conflitti di denominazione e per ottenere l'incapsulamento. Le variabili o gli oggetti dichiarati non sono accessibili al di fuori di questa funzione. Per evitare i problemi di minimizzazione (nomefile.min) utilizzare sempre la funzione auto-eseguita.


1

Le funzioni di auto-esecuzione vengono utilizzate per gestire l'ambito di una variabile.

L'ambito di una variabile è l'area del programma in cui è definita.

Una variabile globale ha portata globale; è definito ovunque nel tuo codice JavaScript ed è accessibile da qualsiasi parte dello script, anche nelle tue funzioni. D'altra parte, le variabili dichiarate all'interno di una funzione sono definite solo all'interno del corpo della funzione. Sono variabili locali, hanno ambito locale e sono accessibili solo all'interno di quella funzione. I parametri di funzione contano anche come variabili locali e sono definiti solo all'interno del corpo della funzione.

Come mostrato di seguito, è possibile accedere alla variabile variabile globale all'interno della funzione e inoltre notare che all'interno del corpo di una funzione, una variabile locale ha la precedenza su una variabile globale con lo stesso nome.

var globalvar = "globalvar"; // this var can be accessed anywhere within the script

function scope() {
    alert(globalvar);
    localvar = "localvar" //can only be accessed within the function scope
}

scope(); 

Quindi sostanzialmente una funzione di auto-esecuzione consente di scrivere il codice senza preoccuparsi di come le variabili sono nominate in altri blocchi di codice javascript.


1
(function(){
    var foo = {
        name: 'bob'
    };
    console.log(foo.name); // bob
})();
console.log(foo.name); // Reference error

In realtà, la funzione sopra sarà trattata come espressione di funzione senza un nome.

Lo scopo principale di avvolgere una funzione con parentesi chiusa e aperta è quello di evitare di inquinare lo spazio globale.

Le variabili e le funzioni all'interno dell'espressione della funzione sono diventate private (ovvero) non saranno disponibili al di fuori della funzione.


1

La risposta breve è: prevenire l'inquinamento dell'ambito globale (o superiore).

IIFE (Espressioni di funzione immediatamente invocate) è la migliore pratica per scrivere script come plug-in, componenti aggiuntivi, script utente o qualunque script che dovrebbe funzionare con gli script di altre persone . Ciò garantisce che qualsiasi variabile definita non dia effetti indesiderati su altri script.

Questo è l'altro modo di scrivere l'espressione IIFE. Personalmente preferisco questo seguente metodo:

void function() {
  console.log('boo!');
  // expected output: "boo!"
}();

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

Dall'esempio sopra è molto chiaro che IIFE può anche influenzare l'efficienza e le prestazioni, perché la funzione che dovrebbe essere eseguita una sola volta verrà eseguita una volta e poi scaricata per sempre nel vuoto . Ciò significa che la dichiarazione di funzione o metodo non rimane in memoria.


Bello, non ne avevo mai visto voidprima. Mi piace.
Ej.

1

Prima devi visitare MDN IIFE , ora alcuni punti a riguardo

  • questa è Espressione di funzione immediatamente richiamata . Quindi quando il tuo file javascript ha invocato da HTML questa funzione ha chiamato immediatamente.
  • Ciò impedisce l'accesso alle variabili all'interno dell'idioma IIFE e l'inquinamento dell'ambito globale.

0

Sembra che a questa domanda sia stata data una risposta pronta, ma posterò comunque il mio contributo.

So quando mi piace usare le funzioni di auto-esecuzione.

var myObject = {
    childObject: new function(){
        // bunch of code
    },
    objVar1: <value>,
    objVar2: <value>
}

La funzione mi consente di utilizzare un codice aggiuntivo per definire gli attributi e le proprietà childObjects per un codice più pulito, come l'impostazione di variabili comunemente utilizzate o l'esecuzione di equazioni matematiche; Oh! o controllo degli errori. anziché essere limitato alla sintassi di istanza di oggetti nidificati di ...

object: {
    childObject: {
        childObject: {<value>, <value>, <value>}
    }, 
    objVar1: <value>,
    objVar2: <value>
}

La codifica in generale ha molti modi oscuri di fare molte delle stesse cose, facendoti chiederti: "Perché preoccuparsi?" Ma continuano a comparire nuove situazioni in cui non puoi più fare affidamento solo sui principi di base / core.


0

Data la tua semplice domanda: "In javascript, quando vorresti usare questo: ..."

Mi piacciono le risposte di @ken_browning e @ sean_holding, ma ecco un altro caso d'uso che non vedo menzionato:

let red_tree = new Node(10);

(async function () {
    for (let i = 0; i < 1000; i++) {
        await red_tree.insert(i);
    }
})();

console.log('----->red_tree.printInOrder():', red_tree.printInOrder());

dove Node.insert è un'azione asincrona.

Non posso semplicemente chiamare wait in attesa senza la parola chiave asincrona alla dichiarazione della mia funzione e non ho bisogno di una funzione con nome per un uso successivo, ma devo attendere quella chiamata insert o ho bisogno di altre funzionalità più ricche (chi lo sa?) .


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.