Qual è lo scopo di avvolgere interi file Javascript in funzioni anonime come “(function () {…}) ()”?

Ultimamente sto leggendo un sacco di Javascript e ho notato che l’intero file è avvolto come il seguente nei file .js per essere importato.

(function() { ... code ... })(); 

Qual è la ragione per fare questo piuttosto che un semplice insieme di funzioni di costruzione?

Solitamente è nello spazio dei nomi (vedere più avanti) e controlla la visibilità delle funzioni e / o delle variabili dei membri. Pensalo come una definizione dell’object. I plugin jQuery sono solitamente scritti in questo modo.

In Javascript, puoi nidificare le funzioni. Quindi, il seguente è legale:

 function outerFunction() { function innerFunction() { // code } } 

Ora puoi chiamare outerFunction() , ma la visibilità di innerFunction() è limitata all’ambito di outerFunction() , il che significa che è privato a outerFunction() . In pratica segue lo stesso principio delle variabili in Javascript:

 var globalVariable; function someFunction() { var localVariable; } 

corrispondentemente:

 function globalFunction() { var localFunction1 = function() { //I'm anonymous! But localFunction1 is a reference to me! }; function localFunction2() { //I'm named! } } 

Nello scenario sopra, puoi chiamare globalFunction() da qualsiasi luogo, ma non puoi chiamare localFunction1 o localFunction2 .

Cosa stai facendo quando scrivi (function() { ... code ... })() , stai facendo il codice all’interno di una funzione letterale (cioè l’intero “object” è in realtà una funzione). Dopo di ciò, stai invocando la funzione (il finale () ). Quindi il vantaggio principale di questo, come ho detto prima, è che puoi avere metodi / proprietà e proprietà private:

 (function() { var private_var; function private_function() { //code } })() 

Nel primo esempio, globalFunction () era la funzione pubblica che poteva essere chiamata per accedere alla funzionalità pubblica, ma nell’esempio sopra come si chiama? Qui la funzione auto-invocazione fa sì che il codice venga eseguito automaticamente all’avvio. Proprio come è ansible aggiungere initMyStuff (); all’inizio di qualsiasi file .js e verrà eseguito automaticamente come parte dell’ambito globale, questa funzione auto-invocante verrà eseguita automaticamente anche se, trattandosi di una funzione senza nome, non può essere chiamata più volte come potrebbe essere initMyStuff ().

La cosa bella è che puoi anche definire le cose dentro e esporle al mondo esterno così (un esempio di namespaces così puoi praticamente creare la tua libreria / plugin):

 var myPlugin = (function() { var private_var; function private_function() { } return { public_function1: function() { }, public_function2: function() { } } })() 

Ora puoi chiamare myPlugin.public_function1() , ma non puoi accedere a private_function() ! Quindi abbastanza simile a una definizione di class. Per capirlo meglio, raccomando i seguenti collegamenti per ulteriori letture:

  • Namespacing il tuo Javascript
  • Membri privati ​​in Javascript (di Douglas Crockford)

MODIFICARE

Ho dimenticato di dirlo. In quel finale () , puoi passare tutto ciò che vuoi dentro. Ad esempio, quando crei i plugin jQuery, passi in jQuery o $ modo:

 (function(jQ) { ... code ... })(jQuery) 

Quindi quello che stai facendo qui è definire una funzione che accetta un parametro (chiamato jQ , una variabile locale e noto solo a quella funzione). Quindi stai auto-invocando la funzione e passando un parametro (chiamato anche jQuery , ma questo è dal mondo esterno e un riferimento alla stessa jQuery effettiva). Non c’è bisogno urgente di farlo, ma ci sono alcuni vantaggi:

  • È ansible ridefinire un parametro globale e assegnargli un nome che abbia senso nell’ambito locale.
  • C’è un leggero vantaggio in termini di prestazioni poiché è più veloce cercare le cose nello scope locale invece di dover risalire la catena dell’oscilloscopio nell’ambito globale.
  • Ci sono benefici per la compressione (minificazione).

In precedenza ho descritto come queste funzioni vengono eseguite automaticamente all’avvio, ma se vengono eseguite automaticamente chi passa gli argomenti? Questa tecnica presuppone che tutti i parametri siano definiti come variabili globali. Quindi, se jQuery non è stato definito come variabile globale, questo esempio non funzionerebbe e non potrebbe essere chiamato in nessun altro modo poiché il nostro esempio è una funzione anonima. Come puoi immaginare, una cosa che jquery.js fa durante l’inizializzazione è definire una variabile globale ‘jQuery’, così come la più famosa variabile ‘$’ globale, che permette a questo codice di funzionare dopo che jquery.js è incluso.

In breve

Sommario

Nella sua forma più semplice, questa tecnica mira a racchiudere il codice all’interno di un ambito di funzione .

Aiuta a ridurre le possibilità di:

  • scontrandosi con altre applicazioni / librerie
  • ambito inquinante superiore (a livello globale più probabile)

Non rileva quando il documento è pronto – non è un qualche tipo di document.onload o window.onload

È comunemente noto come Immediately Invoked Function Expression (IIFE) o Self Executing Anonymous Function Immediately Invoked Function Expression (IIFE) .

Codice spiegato

 var someFunction = function(){ console.log('wagwan!'); }; (function() { /* function scope starts here */ console.log('start of IIFE'); var myNumber = 4; /* number variable declaration */ var myFunction = function(){ /* function variable declaration */ console.log('formidable!'); }; var myObject = { /* object variable declaration */ anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; console.log('end of IIFE'); })(); /* function scope ends */ someFunction(); // reachable, hence works: see in the console myFunction(); // unreachable, will throw an error, see in the console myObject.anotherFunc(); // unreachable, will throw an error, see in the console 

Nell’esempio sopra, qualsiasi variabile definita nella funzione (cioè dichiarata usando var ) sarà “privata” e accessibile all’interno dell’ambito della funzione SOLO (come dice Vivin Paliath). In altre parole, queste variabili non sono visibili / raggiungibili al di fuori della funzione. Guarda la demo dal vivo .

Javascript ha scope della funzione. “I parametri e le variabili definiti in una funzione non sono visibili al di fuori della funzione e una variabile definita ovunque all’interno di una funzione è visibile ovunque all’interno della funzione.” (da “Javascript: Le buone parti”).


Più dettagli

Codice alternativo

Alla fine, il codice pubblicato prima potrebbe anche essere eseguito come segue:

 var someFunction = function(){ console.log('wagwan!'); }; var myMainFunction = function() { console.log('start of IIFE'); var myNumber = 4; var myFunction = function(){ console.log('formidable!'); }; var myObject = { anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; console.log('end of IIFE'); }; myMainFunction(); // I CALL "myMainFunction" FUNCTION HERE someFunction(); // reachable, hence works: see in the console myFunction(); // unreachable, will throw an error, see in the console myObject.anotherFunc(); // unreachable, will throw an error, see in the console 

Guarda la demo dal vivo .


Le radici

Iterazione 1

Un giorno, qualcuno probabilmente pensò “ci deve essere un modo per evitare di nominare” myMainFunction “, poiché tutto ciò che vogliamo è eseguirlo immediatamente”.

Se torni alle basi, scopri che:

  • expression : qualcosa che valuta un valore. cioè 3+11/x
  • statement : riga (e) di codice che fa qualcosa, ma non valuta un valore. vale a dire if(){}

Allo stesso modo, le espressioni di funzione valutano un valore. E una conseguenza (suppongo?) È che possano essere immediatamente invocati:

  var italianSayinSomething = function(){ console.log('mamamia!'); }(); 

Quindi il nostro esempio più complesso diventa:

 var someFunction = function(){ console.log('wagwan!'); }; var myMainFunction = function() { console.log('start of IIFE'); var myNumber = 4; var myFunction = function(){ console.log('formidable!'); }; var myObject = { anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; console.log('end of IIFE'); }(); someFunction(); // reachable, hence works: see in the console myFunction(); // unreachable, will throw an error, see in the console myObject.anotherFunc(); // unreachable, will throw an error, see in the console 

Guarda la demo dal vivo .

Iterazione 2

Il passo successivo è il pensiero “perché var myMainFunction = se non lo usiamo nemmeno !?”.

La risposta è semplice: prova a rimuovere questo, come di seguito:

  function(){ console.log('mamamia!'); }(); 

Guarda la demo dal vivo .

Non funzionerà perché “le dichiarazioni di funzione non sono richiamabili” .

Il trucco è che rimuovendo var myMainFunction = abbiamo trasformato l’ espressione della funzione in una dichiarazione di funzione . Vedi i collegamenti in “Risorse” per maggiori dettagli su questo.

La prossima domanda è “perché non posso tenerlo come espressione di una funzione con qualcosa di diverso da var myMainFunction = ?

La risposta è “puoi”, e ci sono in realtà molti modi per farlo: aggiungere un + , un ! , a - , o magari avvolgendo una coppia di parentesi (come ormai avviene per convenzione), e più credo. Per esempio:

  (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console. 

o

  +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console 

o

  -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console 
  • Cosa fa il punto esclamativo prima della funzione?
  • JavaScript più segno davanti al nome della funzione

Quindi, una volta aggiunta la modifica pertinente a quello che era il nostro “Codice alternativo”, ritorniamo allo stesso codice identico a quello utilizzato nell’esempio “Codice spiegato”

 var someFunction = function(){ console.log('wagwan!'); }; (function() { console.log('start of IIFE'); var myNumber = 4; var myFunction = function(){ console.log('formidable!'); }; var myObject = { anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; console.log('end of IIFE'); })(); someFunction(); // reachable, hence works: see in the console myFunction(); // unreachable, will throw an error, see in the console myObject.anotherFunc(); // unreachable, will throw an error, see in the console 

Maggiori informazioni su Expressions vs Statements :

  • developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators
  • developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Function_constructor_vs._function_declaration_vs._function_expression
  • Javascript: differenza tra una frase e un’espressione?
  • Espressione contro dichiarazione

Scopo demistificante

Una cosa che ci si potrebbe chiedere è “cosa succede quando NON si definisce la variabile ‘correttamente’ all’interno della funzione, ad esempio si esegue invece un semplice compito?”

 (function() { var myNumber = 4; /* number variable declaration */ var myFunction = function(){ /* function variable declaration */ console.log('formidable!'); }; var myObject = { /* object variable declaration */ anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; myOtherFunction = function(){ /* oops, an assignment instead of a declaration */ console.log('haha. got ya!'); }; })(); myOtherFunction(); // reachable, hence works: see in the console window.myOtherFunction(); // works in the browser, myOtherFunction is then in the global scope myFunction(); // unreachable, will throw an error, see in the console 

Guarda la demo dal vivo .

In sostanza, se a una variabile che non è stata dichiarata nel suo ambito corrente viene assegnato un valore, “si verifica una catena di scope fino a quando non trova la variabile o raggiunge l’ambito globale (a quel punto verrà creato)”.

Quando in un ambiente browser (rispetto a un ambiente server come nodejs) l’ambito globale viene definito dall’object window . Quindi possiamo fare window.myOtherFunction() .

Il mio consiglio sulle “buone pratiche” su questo argomento è di usare sempre var durante la definizione di qualsiasi cosa : se si tratta di un numero, di un object o di una funzione, e anche quando si trova nell’ambito globale. Questo rende il codice molto più semplice.

Nota:

  • javascript non ha block scope (Aggiornamento: variabili locali dell’ambito del blocco aggiunte in ES6 .)
  • javascript ha solo function scope e function scope global scope (ambito window in un ambiente browser)

Maggiori informazioni su Javascript Scopes :

  • Qual è lo scopo della parola chiave var e quando usarlo (o ometterlo)?
  • Qual è lo scopo delle variabili in JavaScript?

risorse

  • youtu.be/i_qE1iAmjFg?t=2m15s – Paul Irish presenta l’IIFE alle 2:15 min, guarda questo!
  • developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
  • Libro: Javascript, le parti buone – altamente raccomandato
  • youtu.be/i_qE1iAmjFg?t=4m36s – Paul Irish presenta il modello del modulo alle 4:36

Prossimi passi

Una volta ottenuto questo concetto di IIFE , questo porta al module pattern del module pattern , che viene comunemente fatto sfruttando questo modello IIFE. Divertiti 🙂

Javascript in un browser ha solo un paio di ambiti efficaci: l’ambito delle funzioni e l’ambito globale.

Se una variabile non è nell’ambito della funzione, è nell’ambito globale. E le variabili globali sono generalmente cattive, quindi questo è un costrutto per mantenere le variabili di una libreria su se stesso.

Si chiama chiusura. In pratica sigilla il codice all’interno della funzione in modo che altre librerie non interferiscano con esso. È simile alla creazione di uno spazio dei nomi in lingue compilate.

Esempio. Supponiamo che scriva:

 (function() { var x = 2; // do stuff with x })(); 

Ora altre librerie non possono accedere alla variabile x ho creato per utilizzare nella mia libreria.

È ansible utilizzare le chiusure di funzioni come dati anche in espressioni più grandi, come in questo metodo di determinazione del supporto del browser per alcuni degli oggetti html5.

  navigator.html5={ canvas: (function(){ var dc= document.createElement('canvas'); if(!dc.getContext) return 0; var c= dc.getContext('2d'); return typeof c.fillText== 'function'? 2: 1; })(), localStorage: (function(){ return !!window.localStorage; })(), webworkers: (function(){ return !!window.Worker; })(), offline: (function(){ return !!window.applicationCache; })() } 

Oltre a mantenere le variabili locali, un uso molto utile è quando si scrive una libreria utilizzando una variabile globale, è ansible assegnargli un nome di variabile più breve da utilizzare all’interno della libreria. Viene spesso utilizzato nella scrittura di plugin jQuery, poiché jQuery consente di disabilitare la variabile $ che punta a jQuery, usando jQuery.noConflict (). Nel caso in cui sia disabilitato, il tuo codice può ancora usare $ e non rompere se lo fai semplicemente:

 (function($) { ...code...})(jQuery); 
  1. Per evitare lo scontro con altri metodi / librerie nella stessa finestra,
  2. Evita l’ambito globale, rendilo ambito locale,
  3. Per rendere il debugging più veloce (ambito locale),
  4. JavaScript ha solo funzione scope, quindi aiuterà anche nella compilazione dei codici.

Dovremmo anche usare ‘use strict’ nella funzione scope per assicurarci che il codice debba essere eseguito in “strict mode”. Codice di esempio mostrato di seguito

 (function() { 'use strict'; //Your code from here })();