Umfang
Umfang ist der Funktionsbereich einer Variablen und Funktion. Alle in einer Funktion in JavaScript deklarierten Variablen sind im Funktionskörper immer sichtbar. Es gibt globale und lokale Bereiche in JavaScript, aber es gibt keinen Umfang auf Blockebene. Die Priorität lokaler Variablen ist höher als die der globalen Variablen. In mehreren Beispielen können wir die "unausgesprochenen Regeln" des Umfangs in JavaScript verstehen (dies sind auch Fragen, die häufig in Front-End-Interviews gestellt werden).
1. Variable Deklaration im Voraus
Beispiel 1:
var scope = "global"; function Scopetest () {console.log (Scope); var scope = "Local"} Scopetest (); //undefiniertDie Ausgabe hier ist undefiniert und es gibt keinen Fehler. Dies liegt daran, dass die Deklarationen in der oben erwähnten Funktion im Funktionskörper immer sichtbar sind. Die obige Funktion entspricht:
var scope = "global"; function Scopetest () {var scope; console.log (scope); Scope = "Local"} Scopetest (); // lokalBeachten Sie, dass die Variable, wenn VAR vergessen wird, als globale Variable deklariert wird.
2. Kein Umfang auf Blockebene
Im Gegensatz zu anderen Sprachen, die wir häufig verwenden, gibt es in JavaScript keinen Umfang auf Blockebene:
Funktion Scopetest () {var scope = {}; if (scope instanceof object) {var j = 1; für (var i = 0; i <10; i ++) {//console.log(i); } console.log (i); // Ausgabe 10} console.log (j); // Ausgabe 1}In JavaScript ist der Funktionsumfang der Variablen Funktionsebene, dh alle Variablen in der Funktion sind in der gesamten Funktion definiert, was auch einige "unausgesprochene Regeln" bringt, auf die wir begegnen, wenn wir nicht vorsichtig sind:
var scope = "hello"; function Scopetest () {console.log (Scope); // ① var scope = "no"; console.log (scope); // ②}Der Wertausgang bei ① war tatsächlich undefiniert, was verrückt ist. Wir haben den Wert der globalen Variablen definiert. Sollte dieser Ort nicht Hallo sein? Tatsächlich entspricht der obige Code:
var scope = "hello"; function Scopetest () {var scope; console.log (scope); // ① scope = "no"; console.log (scope); // ②}Deklare frühe und globale Variablen haben eine geringere Priorität als lokale Variablen. Nach diesen beiden Regeln ist es nicht schwer zu verstehen, warum die Ausgabe undefiniert ist.
Bereichskette
In JavaScript hat jede Funktion ihren eigenen Ausführungskontext. Wenn der Code in dieser Umgebung ausgeführt wird, wird eine Bereichskette von variablen Objekten erstellt. Die Bereichskette ist eine Objektliste oder eine Objektkette, die den geordneten Zugriff auf variable Objekte gewährleistet.
Das vordere Ende der Bereichskette ist das variable Objekt der aktuellen Code -Ausführungsumgebung, die häufig als "aktives Objekt" bezeichnet wird. Die Suche nach Variablen startet vom Objekt der ersten Kette. Wenn das Objekt variable Attribute enthält, wird die Suche gestoppt. Wenn nicht, sucht die Suche weiter nach der überlegenen Bereichskette, bis das globale Objekt gefunden wird:
Die Suche nach Zielfernrohrketten schrittweise wirkt sich auch auf die Leistung des Programms aus. Je länger die Variablenkette der Umfang ist, desto größer ist der Einfluss auf die Leistung. Dies ist auch ein Hauptgrund, warum wir versuchen, globale Variablen zu vermeiden.
Schließung
Grundkonzepte
Scope ist eine Voraussetzung für das Verständnis von Schließungen. Verschlüsse beziehen sich auf die Fähigkeit, im externen Bereich innerhalb des aktuellen Bereichs auf Variablen zugreifen zu können.
function createCLosure () {var name = "Jack"; return {setStr: function () {name = "rose"; }, getstr: function () {return name + ": Hallo"; }}} var builder = new createCLosure (); builder.setstr (); console.log (builder.getStr ()); // Rose: HalloDas obige Beispiel gibt zwei Schließungen in der Funktion zurück, die beide Verweise auf den externen Bereich aufrechterhalten, sodass Variablen in der externen Funktion immer zugänglich sind, wo immer sie aufgerufen werden. In einer Funktion definierte Funktionen fügen das aktive Objekt der externen Funktion zu einer eigenen Bereichskette hinzu. Daher kann im obigen Beispiel die interne Funktion durch die interne Funktion auf die Eigenschaften der externen Funktion zugreifen. Dies ist auch eine Möglichkeit für JavaScript, private Variablen zu simulieren.
Hinweis: Da Verschluss zusätzliche Funktionen von Funktionen haben (interne anonyme Funktionen tragen Bereiche externer Funktionen), belegen Verschlüsse mehr Speicherplatz als andere Funktionen, und übermäßiger Gebrauch kann zu einer erhöhten Speicherverwendung führen.
Variablen in Schließungen
Bei der Verwendung von Schließungen kann der Verschluss aufgrund des Einflusses des Umfangskettenmechanismus nur den letzten Wert der internen Funktion erhalten. Ein Nebeneffekt davon ist, dass der Wert der Variablen immer der letzte Wert ist, wenn sich die interne Funktion in einer Schleife befindet.
// Diese Instanz ist nicht angemessen und hat bestimmte Verzögerungsfaktoren. Dies soll hauptsächlich die Probleme in der Timemanage () {für (var i = 0; i <5; i ++) {setTimeout (function () {console.log (i);}, 1000)} veranschaulichen; }Das obige Programm gibt nicht die Zahlen 1-5 ein, wie wir es erwartet hatten, sondern gibt 5 Mal 5 Mal aus. Schauen wir uns ein anderes Beispiel an:
function createCLosure () {var result = []; für (var i = 0; i <5; i ++) {result [i] = function () {return i; }} Rückgabeergebnis;}CreateCloscuse () [0] () gibt 5 zurück, und createCloscuse () [4] () Returns Value Is 5. Aus den beiden oben genannten Beispielen können wir sehen, dass das Problem bei der Verwendung interner Funktionen mit Schleifen vorhanden ist: Weil die SCOPE -Kette jeder Funktion aktive Objekte für externe Funktionen speichert (Timemanage, Timemanage, CreateCrosclosy). Wenn die externe Funktion zurückgibt, beträgt der Wert von i zu diesem Zeitpunkt 5, so dass der Wert jeder internen Funktion I ebenfalls 5 beträgt.
Wie kann man dieses Problem lösen? Wir können die Rückkehr des erwarteten Ergebniss durch eine anonyme Wrapper (anonyme Selbstausnahmefunktion Ausdruck) erzwingen:
Funktion Timemanage () {für (var i = 0; i <5; i ++) {(Funktion (num) {setTimeout (function () {console.log (num);}, 1000);}) (i); }}Oder geben Sie eine anonyme Funktionszuweisung in der anonymen Funktion des Verschlusss zurück:
Funktion Timemanage () {für (var i = 0; i <10; i ++) {setTimeout ((Funktion (e) {return function () {console.log (e);}}) (i), 1000)}} // Timemanager (); Ausgabe 1,2,3,4,5function createClosure () {var result = []; für (var i = 0; i <5; i ++) {result [i] = function (num) {return function () {console.log (num); } }(ich); } Rückgabeergebnis;} // createCLosure () [1] () Ausgabe 1; CreateClous () [2] () Ausgabe 2Egal, ob es sich um eine anonyme Wrapper oder eine verschachtelte anonyme Funktionen handelt, im Prinzip, da die Funktion nach Wert übergeben wird, wird der Wert der Variablen I an die tatsächliche Parameternummer kopiert, und eine anonyme Funktion wird in der anonymen Funktion erstellt, um die NUM zurückzugeben, so dass jede Funktion eine Kopie der Num hat, die sich nicht auswirkt.
Dies schließt
Achten Sie besonders darauf, wenn Sie dies in Schließungen verwenden, da ein wenig Nachlässigkeit Probleme verursachen kann. Normalerweise verstehen wir, dass dieses Objekt zur Laufzeit an die Funktion gebunden ist. In der globalen Funktion ist dieses Objekt ein Fensterobjekt. Wenn die Funktion als Methode im Objekt aufgerufen wird, entspricht dies diesem Objekt (Todo führt dazu einen Sortiervorgang durch). Da der Umfang der anonymen Funktionen global ist, weist dieser Verschluss normalerweise auf das globale Objektfenster hin:
var scope = "global"; var Object = {Scope: "Local", getScope: function () {return function () {return this.scope; }}}Das Aufrufen von Object.getScope () () gibt den Wert global anstelle der lokalen zurück, die wir erwartet haben. Wir sagten früher, dass die internen anonymen Funktionen im Verschluss den Umfang der externen Funktion tragen werden. Warum also nicht diese der externen Funktion erhalten? Wenn jede Funktion aufgerufen wird, werden diese und Argumente automatisch erstellt. Bei der Suche nach internen anonymen Funktionen suchen sie nach den gewünschten Variablen im aktiven Objekt. Hören Sie daher auf, nach externen Funktionen zu suchen, und es ist nie möglich, in externen Funktionen direkt auf Variablen zuzugreifen. Kurz gesagt, wenn eine Funktion als Methode eines Objekts in einem Verschluss bezeichnet wird, ist es wichtig, besondere Aufmerksamkeit zu schenken, dass dies in der anonymen Funktion innerhalb der Methode auf eine globale Variable weist.
Glücklicherweise können wir dieses Problem sehr einfach lösen. Lagern Sie dies einfach im Bereich der externen Funktion in einer Variablen, auf die durch einen Verschluss zugegriffen werden kann:
var scope = "global"; var Object = {Scope: "Local", getScope: function () {var that = this; return function () {return that.scope; }}} Object.getScope () () () gibt den Wert lokal zurück.Speicher und Leistung
Da der Verschluss die gleiche Rahmenkettenreferenz wie der Funktionslaufzeitkontext enthält, hat er einen bestimmten negativen Effekt. Wenn das aktive Objekt und der Laufzeitkontext in der Funktion zerstört werden, kann das aktive Objekt nicht zerstört werden, da immer noch ein Hinweis auf das aktive Objekt vorliegt, was bedeutet, dass der Verschluss mehr Speicherplatz als normale Funktionen einnimmt und wie folgt auch Speicherverlust verursacht kann:
Funktion BindEvent () {var target = document.getElementById ("elem"); target.onclick = function () {console.log (target.Name); }}Im obigen Beispiel generiert die anonyme Funktion einen Verweis auf das externe Objektziel. Solange die anonyme Funktion existiert, verschwindet die Referenz nicht und das Zielobjekt der externen Funktion wird nicht zerstört, was eine kreisförmige Referenz erzeugt. Die Lösung besteht darin, kreisförmige Verweise auf externe Variablen zu reduzieren, indem eine Kopie von Target erstellt wird. NAME und das Objekt manuell zurücksetzen:
Funktion BindEvent () {var target = document.getElementById ("elem"); var name = target.name; target.onclick = function () {console.log (name); } target = null; }Wenn der Verschluss Zugriff auf externe Variablen gibt, wird der Suchpfad für Kennungen zweifellos hinzugefügt, und unter bestimmten Umständen führt dies auch zu Leistungsverlusten. Wir haben früher erwähnt: Versuchen Sie, externe Variablen in lokale Variablen zu speichern, um die Suchlänge der Bereichsbereiche zu verringern.
Zusammenfassung: Schließungen sind nicht nur für JavaScript, aber sie haben ihre eigenen einzigartigen Manifestationen in JavaScript. Mit Schließungen können wir einige private Variablen in JavaScript definieren und sogar Blockebene imitieren. Während des Einsatzes von Schließungen müssen wir jedoch auch die vorhandenen Probleme verstehen, um unnötige Probleme zu vermeiden.