Come funziona questa sintassi JavaScript / jQuery: (funzione (finestra, non definita) {}) (finestra)?


153

Hai mai dato un'occhiata al codice sorgente di jQuery 1.4 e hai notato come è incapsulato nel modo seguente:

(function( window, undefined ) {

  //All the JQuery code here 
  ...

})(window);

Ho letto un articolo su JavaScript Namespacing e un altro intitolato " An Important Pair of Parens ", quindi ne so qualcosa di quello che sta succedendo qui.

Ma non ho mai visto questa sintassi particolare prima. Cosa undefinedci fa lì? E perché windowdeve essere passato e poi apparire di nuovo alla fine?


3
Volevo aggiungere che Paul Irish ne parla in questo video: paulirish.com/2010/10-things-i-learned-from-the-jquery-source
Mottie

@Bergi hai contrassegnato la mia domanda come duplicato di un altro, ma ho posto la domanda quasi un anno prima del duplicato. Il cast dovrebbe essere al contrario.
Dkinzer,

@dkinzer: non si tratta di chiedere prima, si tratta di una risposta di alta qualità. Certo, in questo caso è collo e collo, ma ho trovato la risposta di CMS per essere la migliore. Vieni a chat.stackoverflow.com/rooms/17 per discuterne, però
Bergi,

Risposte:


161

Il non definito è una variabile normale e può essere modificato semplicemente con undefined = "new value";. Quindi jQuery crea una variabile locale "non definita" che è VERAMENTE indefinita.

La variabile finestra viene resa locale per motivi di prestazioni. Perché quando JavaScript cerca una variabile, passa prima attraverso le variabili locali fino a quando non trova il nome della variabile. Quando non viene trovato, JavaScript passa attraverso l'ambito successivo ecc. Fino a quando non filtra attraverso le variabili globali. Quindi, se la variabile della finestra viene resa locale, JavaScript può cercarla più rapidamente. Ulteriori informazioni: Velocizza il tuo JavaScript - Nicholas C. Zakas


1
Grazie! quello era un video molto istruttivo. Penso che devi modificare la tua risposta per dire "la variabile della finestra è resa locale". Penso che questa sia la risposta migliore. Ho contato e ho scoperto che l'oggetto finestra viene chiamato almeno 17 volte nel codice sorgente di JQuery. Quindi ci deve essere un effetto significativo nel spostare la finestra in ambito locale.
Dkinzer,

@DKinzer Oh sì, sei giusto, ovviamente è reso locale. Sono contento di poterti aiutare =)
Vincent,

1
Secondo questo test aggiornato jsperf.com/short-scope/5 passare 'window' è in realtà più lento, quando si arriva a ottenere una costante di finestra attraverso jQuery, e particolarmente male per Safari. Quindi, sebbene "non definito" abbia un caso d'uso, non sono del tutto convinto che "finestra" sia particolarmente utile in tutti i casi.
hexalys,

1
Ha anche un effetto positivo per minimizzare il codice. Quindi ogni utilizzo di "finestra" e "non definito" può essere sostituito con un nome più breve dal minificatore.
TLindig,

2
@ lukas.pukenis Quando crei una libreria che viene utilizzata in milioni di siti Web, è saggio non fare ipotesi su cosa faranno i pazzi.
JLRishe,

54

Non definito

Dichiarando undefinedcome argomento ma senza mai passare un valore ad esso, si assicura che sia sempre indefinito, in quanto è semplicemente una variabile nell'ambito globale che può essere sovrascritta. Questo rende a === undefinedun'alternativa sicura a typeof a == 'undefined', che salva alcuni personaggi. Inoltre, rende il codice più intuitivo, come undefinedpuò essere abbreviato, ad uesempio, salvando alcuni caratteri in più.

Finestra

Il passaggio windowcome argomento mantiene una copia nell'ambito locale, che influisce sulle prestazioni: http://jsperf.com/short-scope . Tutti gli accessi windowdovranno ora spostarsi di un livello in meno nella catena di portata. Come nel caso undefined, una copia locale consente nuovamente una minificazione più aggressiva.


Nota a margine:

Anche se questo potrebbe non essere stato l'intenzione degli sviluppatori jQuery, il passaggio windowconsente di integrare più facilmente la libreria in ambienti Javascript lato server, ad esempio node.js , dove non esiste alcun windowoggetto globale . In una situazione del genere, è necessario modificare solo una riga per sostituire l' windowoggetto con un'altra. Nel caso di jQuery, un windowoggetto simulato può essere creato e passato ai fini dello scraping HTML (una libreria come jsdom può farlo).


2
+1 per menzionare Minificazione, vorrei poter +2 per jsperf - La versione ridotta è (function(a,b){})(window);- ae bsono molto più brevi di windowe undefined:)
gnarf

+1 Avevo sentito parlare della catena di portata prima, ma ho dimenticato il termine. Grazie!
alex,

È anche un'alternativa all'utilizzo di void (0) che è stato pensato per lo stesso motivo: void (0) è un modo sicuro per ottenere il valore indefinito.
glmxndr,

"Quello che si dice" è in riferimento a quest'altra domanda: stackoverflow.com/questions/4945265/...
gnarf

@gnarf, grazie per la notifica che le domande sono state unite. Modificherò la mia risposta per dare un po 'più senso;)
David Tang,

16

Altri hanno spiegato undefined. undefinedè come una variabile globale che può essere ridefinita su qualsiasi valore. Questa tecnica serve a prevenire la rottura di tutti i controlli indefiniti se qualcuno ha scritto undefined = 10da qualche parte. Un argomento che non viene mai passato è garantito essere reale undefinedindipendentemente dal valore della variabile undefined .

Il motivo per passare la finestra può essere illustrato con il seguente esempio.

(function() {
   console.log(window);
   ...
   ...
   ...
   var window = 10;
})();

Cosa registra la console? Il valore windowdell'oggetto giusto? Sbagliato! 10? Sbagliato! Registra undefined. L'interprete Javascript (o il compilatore JIT) lo riscrive in questo modo -

(function() {
   var window; //and every other var in this function

   console.log(window);
   ...
   ...
   ...
   window = 10;

})();

Tuttavia, se ottieni la windowvariabile come argomento, non c'è var e quindi nessuna sorpresa.

Non so se jQuery lo stia facendo, ma se stai ridefinendo windowla variabile locale in qualsiasi punto della tua funzione per qualsiasi motivo, è una buona idea prenderla in prestito dall'ambito globale.


6

windowviene passato in questo modo nel caso in cui qualcuno decida di ridefinire l'oggetto finestra in IE, presumo lo stesso per undefined, nel caso in cui venga riassegnato in qualche modo in seguito.

La parte superiore windowdi quello script sta semplicemente nominando l'argomento "finestra", un argomento più locale del windowriferimento globale e che cosa utilizzerà il codice all'interno di questa chiusura. Alla windowfine si specifica in realtà cosa passare per il primo argomento, in questo caso l'attuale significato di window... la speranza è che non si sia rovinato windowprima che ciò accada.

Questo può essere più facile da pensare mostrando il caso più tipico usato in jQuery, la .noConflict()gestione dei plugin , quindi per la maggior parte del codice puoi ancora usare $, anche se significa qualcosa di diverso da jQueryquesto ambito:

(function($) {
  //inside here, $ == jQuery, it was passed as the first argument
})(jQuery);

Grazie. Questo ha molto senso. Tuttavia, penso che la risposta sia una combinazione di questo e di ciò che dice @ C.Zakas.
Dkinzer,

@DKinzer Temo di non essere C. Zakas;)
Vincent,

@Vincent, mi dispiace per questo :-)
dkinzer,

5

Testato con 1000000 iterazioni. Questo tipo di localizzazione non ha avuto alcun effetto sulle prestazioni. Neanche un millisecondo in 1000000 iterazioni. Questo è semplicemente inutile.


2
Per non parlare, la chiusura porta tutte le variabili insieme all'istanza della funzione, quindi se hai 100 byte in chiusura e 1000 istanze che sono 100 kb di memoria in più.
Akash Kava,

@AkashKava e @Semra, non credo che questo test spieghi davvero il motivo per cui passeresti alla finestra in una situazione del mondo reale. Il guadagno in termini di prestazioni sarà visibile solo quando si hanno chiusure profondamente annidate che accedono a tale variabile ulteriormente lungo la catena dell'ambito. ovviamente se la tua funzione è solo ad un passo sulla catena dell'oscilloscopio dalla variabile, non vedrai molto guadagno. ma se fosse waaaay laggiù e fosse necessario window, potresti vedere qualche guadagno da una copia locale.
gabereal,

I nuovi motori JavaScript di @gabereal risolvono le chiusure al momento della compilazione stessa, non ci sono guadagni in termini di prestazioni nella chiusura in fase di esecuzione. Dubito che ci sia un guadagno misurabile delle prestazioni, se sei così sicuro, puoi creare l'esempio jsperf per dimostrare le prestazioni. L'unica prestazione che otteniamo è isolando le chiusure che si traducono in una migliore minimizzazione che riduce le dimensioni e le prestazioni si ottengono con download e analisi più veloci dello script.
Akash Kava,

@AkashKava cosa ti fa pensare di essere così fiducioso? Ho appena detto "potrebbe vedere qualche guadagno" e "non credo". Potrei creare un test, ma preferirei non usarlo comunque. puoi spiegare cosa intendi con "risolve le chiusure al momento della compilazione stessa"? al contrario di quale alternativa? come hanno fatto i motori Javascript a fare le cose diversamente prima?
gabereal,

Trova un codice in cui la finestra viene visualizzata alla fine ma non inserita nei parametri della funzione, cosa significa? assicura che i parametri seguano l'oggetto scope globale originale anche se non ci sono parametri di finestra che presumo?
Webwoman
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.