Wie funktioniert diese JavaScript / jQuery-Syntax: (Funktion (Fenster, undefiniert) {}) (Fenster)?

153

Haben Sie sich jemals den jQuery 1.4- Quellcode unter der Haube angesehen und festgestellt, wie er folgendermaßen gekapselt ist:

(function( window, undefined ) {

  //All the JQuery code here 
  ...

})(window);

Ich habe einen Artikel über JavaScript-Namespacing und einen anderen mit dem Titel " Ein wichtiges Paar von Parens " gelesen , damit ich weiß, was hier vor sich geht.

Aber ich habe diese spezielle Syntax noch nie gesehen. Was undefinedmacht das da? Und warum muss windowbestanden werden und dann am Ende wieder erscheinen?

dkinzer
quelle
3
Ich wollte hinzufügen, dass Paul Irish in diesem Video darüber spricht: paulirish.com/2010/10-things-i-learned-from-the-jquery-source
Mottie
@Bergi Sie haben meine Frage als Duplikat einer anderen markiert, aber ich habe die Frage fast ein Jahr vor dem Duplikat gestellt. Die Besetzung sollte umgekehrt sein.
Dkinzer
@dkinzer: Es geht nicht darum, früher gefragt zu werden, es geht um Antworten von höchster Qualität. Zugegeben, in diesem Fall ist es Hals an Hals, aber ich fand die Antwort von CMS die beste. Kommen Sie zu chat.stackoverflow.com/rooms/17 , um dies zu besprechen
Bergi

Antworten:

161

Die undefinierte Variable ist eine normale Variable und kann einfach mit geändert werden undefined = "new value";. JQuery erstellt also eine lokale "undefinierte" Variable, die WIRKLICH undefiniert ist.

Die Fenstervariable wird aus Leistungsgründen lokalisiert. Denn wenn JavaScript eine Variable nachschlägt, durchläuft es zuerst die lokalen Variablen, bis es den Variablennamen findet. Wenn es nicht gefunden wird, durchläuft JavaScript den nächsten Bereich usw., bis es die globalen Variablen filtert. Wenn die Fenstervariable lokalisiert wird, kann JavaScript sie schneller nachschlagen. Weitere Informationen: Beschleunigen Sie Ihr JavaScript - Nicholas C. Zakas

Vincent
quelle
1
Vielen Dank! Das war ein sehr informatives Video. Ich denke, Sie müssen Ihre Antwort bearbeiten, um zu sagen, dass die Fenstervariable lokalisiert ist. Ich denke, das ist die beste Antwort. Ich habe gezählt und festgestellt, dass das Fensterobjekt im JQuery-Quellcode mindestens 17 Mal aufgerufen wird. Das Verschieben des Fensters in den lokalen Bereich muss also einen signifikanten Effekt haben.
Dkinzer
@DKinzer Oh ja, du hast Recht, natürlich ist es lokal gemacht. Ich bin froh, dass ich dir helfen konnte =)
Vincent
1
Nach diesem aktualisierten Test ist jsperf.com/short-scope/5 das Übergeben von 'window' tatsächlich langsamer, wenn es darum geht, eine Fensterkonstante über jQuery zu erhalten, und besonders schlecht für Safari. Obwohl "undefiniert" einen Anwendungsfall hat, bin ich nicht ganz davon überzeugt, dass "Fenster" in allen Fällen besonders nützlich ist.
Hexalys
1
Dies wirkt sich auch positiv auf die Minimierung des Codes aus. So kann jede Verwendung von "window" und "undefined" vom Minifyer durch einen kürzeren Namen ersetzt werden.
TLindig
2
@ lukas.pukenis Wenn Sie eine Bibliothek erstellen, die in Millionen von Websites verwendet wird, ist es ratsam, keine Annahmen darüber zu treffen, was Verrückte tun werden.
JLRishe
54

Nicht definiert

Indem Sie undefinedals Argument deklarieren, aber niemals einen Wert an ihn übergeben, stellen Sie sicher, dass er immer undefiniert ist, da es sich lediglich um eine Variable im globalen Bereich handelt, die überschrieben werden kann. Dies ist a === undefinedeine sichere Alternative zu typeof a == 'undefined', die einige Zeichen spart. Dadurch wird der Code auch minifierfreundlicher, da beispielsweise ein paar weitere Zeichen eingespart werden undefinedkönnen u.

Fenster

Beim Übergeben windowals Argument bleibt eine Kopie im lokalen Bereich, was sich auf die Leistung auswirkt: http://jsperf.com/short-scope . Alle Zugriffe auf windowmüssen nun eine Ebene weniger in der Bereichskette verlaufen. Wie bei ermöglicht undefinedeine lokale Kopie wiederum eine aggressivere Minimierung.


Randnotiz:

Obwohl dies möglicherweise nicht die Absicht der jQuery-Entwickler war, windowermöglicht die Übergabe die einfachere Integration der Bibliothek in serverseitige Javascript-Umgebungen, z. B. node.js, in denen kein globales windowObjekt vorhanden ist. In einer solchen Situation muss nur eine Zeile geändert werden, um das windowObjekt durch eine andere zu ersetzen . Im Fall von jQuery kann ein Scheinobjektwindow zum Zwecke des HTML-Scrapings erstellt und übergeben werden (eine Bibliothek wie jsdom kann dies tun).

David Tang
quelle
2
+1 für die Erwähnung von Minification, wünschte, ich könnte +2 für die jsperf - Die minimierte Version ist (function(a,b){})(window);- aund bsind viel kürzer als windowund undefined:)
Gnarf
+1 Ich hatte schon einmal von Scope Chain gehört , aber den Begriff vergessen. Vielen Dank!
alex
Es ist auch eine Alternative zur Verwendung von void (0), die aus demselben Grund gedacht war: void (0) ist ein sicherer Weg, um den undefinierten Wert zu erhalten.
glmxndr
"Was Sie sagen" bezieht sich auf diese andere Frage: stackoverflow.com/questions/4945265/…
Gnarf
@gnarf, danke für die Benachrichtigung, dass die Fragen zusammengeführt wurden. Ich werde meine Antwort bearbeiten, um etwas mehr Sinn zu machen;)
David Tang
16

Andere haben erklärt undefined. undefinedist wie eine globale Variable, die auf einen beliebigen Wert neu definiert werden kann. Diese Technik soll verhindern, dass alle undefinierten Prüfungen unterbrochen werden, wenn jemand undefined = 10irgendwo etwas geschrieben hat. Ein Argument, das niemals übergeben wird, ist garantiert real, undefinedunabhängig vom Wert der Variablen undefined .

Der Grund für das Übergeben des Fensters kann anhand des folgenden Beispiels veranschaulicht werden.

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

Was protokolliert die Konsole? Der Wert des windowObjekts richtig? Falsch! 10? Falsch! Es protokolliert undefined. Der Javascript-Interpreter (oder JIT-Compiler) schreibt es auf diese Weise neu -

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

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

})();

Wenn Sie jedoch die windowVariable als Argument erhalten, gibt es keine var und daher keine Überraschungen.

Ich weiß nicht, ob jQuery dies tut, aber wenn Sie die windowlokale Variable irgendwo in Ihrer Funktion aus irgendeinem Grund neu definieren, ist es eine gute Idee, sie aus dem globalen Bereich auszuleihen.

Chetan S.
quelle
6

windowwird so übergeben, nur für den Fall, dass sich jemand entscheidet, das Fensterobjekt im IE neu zu definieren, nehme ich dasselbe für den undefinedFall an, dass es später auf irgendeine Weise neu zugewiesen wird.

Oben windowin diesem Skript wird nur das Argument "Fenster" genannt, ein Argument, das lokaler ist als die globale windowReferenz und das, was der Code in diesem Abschluss verwendet. Am windowEnde wird tatsächlich angegeben, was für das erste Argument verwendet werden soll, in diesem Fall die aktuelle Bedeutung von window... die Hoffnung ist, dass Sie es nicht vermasselt haben, windowbevor dies geschieht.

Dies ist möglicherweise einfacher zu verstehen, wenn der typischste Fall .noConflict()für die Behandlung von jQuery-Plugins angezeigt wird. Für den Großteil des Codes können Sie ihn also weiterhin verwenden $, auch wenn er etwas anderes als jQueryaußerhalb dieses Bereichs bedeutet:

(function($) {
  //inside here, $ == jQuery, it was passed as the first argument
})(jQuery);
Nick Craver
quelle
Vielen Dank. Das macht sehr viel Sinn. Ich denke jedoch, dass die Antwort eine Kombination aus diesem und dem ist, was @ C.Zakas sagt.
Dkinzer
@DKinzer Ich fürchte, ich bin nicht C. Zakas;)
Vincent
@ Vincent, tut mir leid :-)
Dkinzer
5

Getestet mit 1000000 Iterationen. Diese Art der Lokalisierung hatte keinen Einfluss auf die Leistung. Nicht einmal eine Millisekunde in 1000000 Iterationen. Das ist einfach nutzlos.

Semra
quelle
2
Ganz zu schweigen davon, dass der Abschluss alle Variablen zusammen mit der Funktionsinstanz enthält. Wenn Sie also 100 Bytes im Abschluss haben und 1000 Instanzen, sind das 100 KB mehr Speicher.
Akash Kava
@AkashKava und @Semra, ich glaube nicht, dass dieser Test wirklich erklärt, warum Sie in einer realen Situation im Fenster vorbeikommen würden. Der Leistungsgewinn wird nur sichtbar, wenn Sie tief verschachtelte Abschlüsse haben, die weiter entlang der Bereichskette auf diese Variable zugreifen. Wenn Ihre Funktion in der Bereichskette nur einen Schritt von der Variablen entfernt ist, werden Sie offensichtlich keinen großen Gewinn sehen. Aber wenn es dort unten waaaay war und benötigt wurde, windowkönnten Sie einen Gewinn durch eine lokale Kopie sehen.
gabereal
@gabereal Neue JavaScript-Engines lösen Schließungen zum Zeitpunkt der Kompilierung selbst auf. Es gibt keinen Leistungsgewinn beim Schließen zur Laufzeit. Ich bezweifle, dass es einen messbaren Leistungsgewinn gibt. Wenn Sie so sicher sind, können Sie ein jsperf-Beispiel erstellen, um die Leistung zu demonstrieren. Die einzige Leistung, die wir erhalten, besteht darin, Verschlüsse zu isolieren, was zu einer besseren Minimierung führt, wodurch Größe und Leistung durch schnelleres Herunterladen und Parsen von Skripten reduziert werden.
Akash Kava
@AkashKava Was lässt dich denken, dass ich so zuversichtlich bin ? Ich sagte nur "könnte einen Gewinn sehen" und "ich glaube nicht". Ich könnte einen Test erstellen, aber ich möchte lieber nicht, da ich dieses Muster sowieso nie verwende. Können Sie erklären, was Sie unter "Schließungen zum Zeitpunkt der Kompilierung selbst beheben" verstehen? im Gegensatz zu welcher Alternative? Wie haben Javascript-Engines die Dinge vorher anders gemacht?
gabereal
Haben Sie einen Code gefunden, in dem das Fenster am Ende angezeigt wird, aber nicht in die Parameter der Funktion eingegeben wurde? Was bedeutet das? es stellt sicher, dass die Parameter dem Bereichsobjekt des ursprünglichen Globals folgen, auch wenn es keine Fensterparameter gibt, die ich annehme?
Webwoman