Ich schaue in letzter Zeit das Training von ES2015, es gibt ein Sprichwort, das sagt
In JavaScript gibt es keinen Umfang auf Blockebene
Möglicherweise verstehen Sie dieses Problem nicht. Schauen wir uns zuerst ein Beispiel an
var a = [] für (var i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] ();Ich denke, viele Leute denken, dass das Ergebnis dieser Frage 6 ist, aber leider lautet die Antwort 10. Versuchen Sie etwas anderes. a [7] (), a [8] () und a [8] () alle haben 10 !!
Da JS die primitiven Variablen häufig in entsprechende Objekte umwickelt, beispielsweise für Var Str = "Hello World"; Str.Slice (1).
Der eigentliche Prozess von JS ist wahrscheinlich var str = "Hallo Welt"; neue String (str) .Slice (1). Ein solcher Prozess kann für uns Probleme verursachen, das Problem zu verstehen.
Um dieses Problem hier zu erklären, gehöre ich zum Zahlentyp im primitiven Typ. Ich erkläre es explizit als den Zahlentyp. Da der Zuordnungsprozess des Basistyps darin besteht, den Speicher erneut zu beantragen und die Richtung der Variablen zu ändern, verwenden wir auch den Prozess des RE-NEW-Zahlenobjekts, um diesen Prozess zu simulieren. Der geänderte Code lautet wie folgt:
var a = [] var i = neue Nummer (0); für (; i <10; i = neue Nummer (i+1)) {a [i] = function () {console.log (i.toString ()); }} a [6] (); // 10a [7] (); // 10a [8] (); // 10a [9] (); // 10a [9] (); // 10Schauen wir uns die relativen Speicheradressen dieser Variablen in Kombination mit einem Programm an.
(function () {var id = 0; function generateId () {return id ++;}; Object.prototyp.id = function () {var newId = generateId (); this.id = function () {return Newid;};}; neue Nummer (i+1), id ()) {a [i] = function () {console.log (id ()); console.log (i.tostring ()); }} a [6] (); // 10 10a [7] (); // 10 10a [8] (); // 10 10a [9] (); // 10 10console.log (id ()) // 10Hier haben wir in der Tat den gesamten "Zuweisungs" -Effekt unseres I simuliert, und die relative Adresse von Ich habe mich von 0 auf 10 geändert (am Ende muss es einmal hinzugefügt werden, bevor es aus der für die Schleife herausspringen kann).
Während wir uns die relative Adresse von i ansehen, fanden wir ein Problem: Wenn die Funktion, die einem [x] (x: 0 ~ 9) entspricht, ausgeführt wird, lautet die relative Adresse von I Referenz 10. Warum?
Hier werden wir das Problem der Blockebene einbeziehen. Hier zitieren wir eine Passage von Ruan Yifeng in der Einführung in ES6:
ES5 hat nur den globalen Umfang und den Funktionsumfang, aber keinen Umfang auf Blockebene.
ES5 ist die am häufigsten verwendete Version von JS. Dieser Satz besagt, dass es in JavaScript keinen Blockbereich gibt. Es gibt nur globale Bereiche und Blockebene.
Wie versteht man? Zum Beispiel
für (var i = 0; i <10; i ++) {console.log (i);} console.log (i); // 10console.log (window.i); // 10Intuitiv glauben wir, dass die für die Loop ein Codeblock ist und zu einem Bereich auf Blockebene gehören sollte. Normalerweise kann dies jedoch nicht nur 0 bis 9 ausgeben, sondern auch 10 extern in der für Schleife ausgeben. Gleichzeitig stellten wir fest, dass ich, obwohl wir I auf der für die Loop definiert haben, anscheinend an dem globalen Fensterobjekt hängen (wenn es sich um die Ausführungsumgebung von NodeJs handelt, es an dem globalen Objekt hängt).
Daher haben Blöcke wie für Schleifen in JavaScript nicht den Einfluss eines Blockebens. Das Definieren von Variablen in Codeblöcken wie für Schleifen unterscheidet sich nicht von der direkten Definition von Variablen im aktuellen Bereich.
Aber wir können den Umfang durch Funktionen isolieren:
(function () {for (var i = 0; i <10; i ++) {console.log (i);} console.log (i);}) () console.log (i); //// I ist nicht definiertGleichzeitig, wenn console.log (window.i); wird ausgeführt, das undefinierte Ergebnis wird erzielt. Hier verwenden wir eine sofortige Ausführungsfunktion, um einen Bereich zu bilden. Es spielt eine ähnliche Rolle wie ein Codeblock. Nach diesem Funktionsbereich kann die Variable, auf die ich nicht mehr zugegriffen werden kann. Ich kann jedoch jederzeit innerhalb des Funktionsbereichs zugegriffen werden.
Kehren Sie zu der vorherigen Frage zurück, und dann werden wir sie in Kombination nur mit dem globalen Umfang und dem Umfang auf Blockebene in JavaScript verstehen. In der für Schleife muss das definierte I im aktuellen Bereich definiert werden, dh im Fensterbereich. Im Schleifenkörper weisen wir A [i] eine Funktion zu. Wenn wir diese Funktion ausführen, ist die Situation wie folgt:
Ich existiere nicht in der Funktion, also folgen wir der Bereichskette, um i zu finden. Das Ich gibt zu diesem Zeitpunkt das aus, ist das ich. Da ich aus den letzten +1 der Schleife springe, werde ich zu 10, also ist das Ausgabeergebnis immer 10. Aber was wir wirklich brauchen, ist nicht das letzte, aber ich im Zwischenprozess. Wenn wir dieses Problem lösen wollen, müssen wir die Variable I beiseite legen (weil ich das letzte Mal zu 10 wird). Wir müssen die Funktion, die einer [0] entsprechend entspricht, auf den Wert 0 beziehen und die Funktion, die einem [1] entspricht, auf den Wert 1 beziehen. 1. Wie in der folgenden Abbildung dargestellt:
Gehen Sie zurück zu unserem vorherigen Code.
Der Pfeil in der Abbildung zeigt, dass wir auf I (0 ~ 9) zugreifen können. Da die For-Loop nicht für sich selbst einen Block-Ebene-Bereich bildet, greifen wir auf das von der für die Loop definierte I zu, wenn wir der Bereichskette folgen.
Hier wickeln wir unseren Code mit einer sofortigen Ausführungsfunktion ein, die einen Umfang bilden kann, und wir übergeben den Wert I dafür. Die folgende:
var a = [] var i = neue Nummer (0); Konsole.log (id ()); // 0for (; i <10; i = neue Nummer (i+1), id ()) {(Funktion (i) {a [i] = function () {console.log (id (); console.log (i.tosting (); // 6 6a [7] (); // 7 7a [8] (); // 8 8a [9] (); // 9 9console.log (id ()); // 10}Da sich diese sofortige Ausführungsfunktion auf den numerischen Wert 0 ~ 9 bezieht, werden wir zuerst den Umfang der sofortigen Ausführungsfunktion entlang der Bereichskette finden, wenn wir die Funktion A [i] ausführen. Die sofortige Ausführungsfunktion behält die numerische Referenz von 0 ~ 9 bei und wir können den Wert von I in der Funktion a [i] korrekt ausgeben. Durch das Ausführungsergebnis können wir feststellen, dass nicht nur das Ausführungsergebnis korrekt ist, sondern die relative Speicheradresse des von uns referenzierten Werts ist auch korrekt. Dann ändern wir das Zahlenobjekt, das ursprünglich ausdrücklich für die Prüfung deklariert wurde. Wie folgt:
var a = []; für (var i = 0; i <10; i ++) {(Funktion (i) {a [i] = function () {console.log (i);}}) (i);}Schauen wir uns schließlich die ES6 -Syntax an, die Sie verwenden, um LET anstelle von VAR zu verwenden und den ES5 -Code über Baable zu kompilieren und zu generieren:
// ES6 Code var a = [] für (let i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] (); // babel kompilierte und generierte ES5 -Code "Strict"; var a = []; var _loop = function _loop (i) {a [i] = function () {console.log (i); };}; für (var i = 0; i <10; i ++) {_loop (i);} a [6] ();Mal sehen, ob unsere Lösung der ES6 -Lösung sehr ähnlich ist. Hier entspricht unsere unmittelbare Ausführungsfunktion der Ausführung der Funktion von _loop und _loop (i) im generierten ES5 -Code.