var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
Es gibt Folgendes aus:
Mein Wert: 3
Mein Wert: 3
Mein Wert: 3
Ich möchte, dass es Folgendes ausgibt:
Mein Wert: 0
Mein Wert: 1
Mein Wert: 2
Das gleiche Problem tritt auf, wenn die Verzögerung beim Ausführen der Funktion durch die Verwendung von Ereignis-Listenern verursacht wird:
var buttons = document.getElementsByTagName("button");
// let's create 3 functions
for (var i = 0; i < buttons.length; i++) {
// as event listeners
buttons[i].addEventListener("click", function() {
// each should log its value.
console.log("My value: " + i);
});
}
<button>0</button>
<br />
<button>1</button>
<br />
<button>2</button>
… Oder asynchroner Code, zB mit Promises:
// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
for (var i = 0; i < 3; i++) {
// Log `i` as soon as each promise resolves.
wait(i * 100).then(() => console.log(i));
}
Bearbeiten: Es ist auch in for in
und for of
Schleifen ersichtlich :
const arr = [1,2,3];
const fns = [];
for(i in arr){
fns.push(() => console.log(`index: ${i}`));
}
for(v of arr){
fns.push(() => console.log(`value: ${v}`));
}
for(f of fns){
f();
}
Was ist die Lösung für dieses Grundproblem?
javascript
loops
closures
nickf
quelle
quelle
funcs
sicher kein Array sein, wenn Sie numerische Indizes verwenden? Nur ein Kopf hoch.Antworten:
Das Problem ist, dass die Variable
i
innerhalb jeder Ihrer anonymen Funktionen an dieselbe Variable außerhalb der Funktion gebunden ist.Klassische Lösung: Verschlüsse
Sie möchten die Variable innerhalb jeder Funktion an einen separaten, unveränderlichen Wert außerhalb der Funktion binden:
Da es in JavaScript keinen Blockbereich gibt - nur Funktionsbereich -, indem Sie die Funktionserstellung in eine neue Funktion einschließen, stellen Sie sicher, dass der Wert von "i" wie beabsichtigt bleibt.
ES5.1-Lösung: für jeden
Angesichts der relativ weit verbreiteten Verfügbarkeit der
Array.prototype.forEach
Funktion (im Jahr 2015) ist anzumerken, dass in Situationen, in denen die Iteration hauptsächlich über eine Reihe von Werten erfolgt,.forEach()
eine saubere, natürliche Möglichkeit besteht, für jede Iteration einen eindeutigen Abschluss zu erhalten. Angenommen, Sie haben eine Art Array, das Werte enthält (DOM-Referenzen, Objekte usw.), und das Problem beim Einrichten von Rückrufen, die für jedes Element spezifisch sind, können Sie Folgendes tun:Die Idee ist, dass jeder Aufruf der Rückruffunktion, die mit der
.forEach
Schleife verwendet wird, ein eigener Abschluss ist. Der an diesen Handler übergebene Parameter ist das Array-Element, das für diesen bestimmten Schritt der Iteration spezifisch ist. Wenn es in einem asynchronen Rückruf verwendet wird, kollidiert es nicht mit anderen Rückrufen, die in anderen Schritten der Iteration eingerichtet wurden.Wenn Sie zufällig in jQuery arbeiten,
$.each()
bietet Ihnen die Funktion eine ähnliche Funktion.ES6-Lösung:
let
ECMAScript 6 (ES6) führt neue
let
undconst
Schlüsselwörter ein, deren Gültigkeitsbereich sich vonvar
Variablen auf Basis unterscheidet . Beispielsweise hat in einer Schleife mit einemlet
Index auf Basis jeder Iteration durch die Schleife eine neue Variablei
mit Schleifenbereich, sodass Ihr Code wie erwartet funktioniert. Es gibt viele Ressourcen, aber ich würde den Block-Scoping-Beitrag von 2ality als hervorragende Informationsquelle empfehlen .Beachten Sie jedoch, dass IE9-IE11 und Edge vor Edge 14 unterstützt werden,
let
aber die oben genannten Fehler auftreten (sie erstellen nichti
jedes Mal eine neue , sodass alle oben genannten Funktionen 3 protokollieren würden, wie wenn wir sie verwenden würdenvar
). Edge 14 macht es endlich richtig.quelle
function createfunc(i) { return function() { console.log("My value: " + i); }; }
immer noch Abschluss, weil es die Variable verwendeti
?Function.bind()
ist derzeit definitiv vorzuziehen, siehe stackoverflow.com/a/19323214/785541 ..bind()
ist „die richtige Antwort“ ist nicht richtig. Sie haben jeweils ihren eigenen Platz. Mit können.bind()
Sie keine Argumente binden, ohne denthis
Wert zu binden . Außerdem erhalten Sie eine Kopie desi
Arguments, ohne es zwischen Aufrufen mutieren zu können, was manchmal erforderlich ist. Es handelt sich also um ganz andere Konstrukte, ganz zu schweigen davon, dass die.bind()
Implementierungen historisch langsam waren. Sicher, in dem einfachen Beispiel würde beides funktionieren, aber Verschlüsse sind ein wichtiges Konzept, um es zu verstehen, und darum ging es bei der Frage.Versuchen:
Bearbeiten (2014):
Persönlich denke ich, dass die jüngste Antwort von
.bind
@ Aust über die Verwendung der beste Weg ist, dies jetzt zu tun. Es gibt auch lo-dash / Unterstrich ist ,_.partial
wenn Sie nicht brauchen oder wollen Chaos mitbind
‚sthisArg
.quelle
}(i));
?i
als Argumentindex
an die Funktion.Ein anderer Weg, der noch nicht erwähnt wurde, ist die Verwendung von
Function.prototype.bind
AKTUALISIEREN
Wie von @squint und @mekdev hervorgehoben, erzielen Sie eine bessere Leistung, indem Sie die Funktion zuerst außerhalb der Schleife erstellen und dann die Ergebnisse innerhalb der Schleife binden.
quelle
_.partial
.bind()
wird mit ECMAScript 6-Funktionen weitgehend veraltet sein. Außerdem werden dadurch tatsächlich zwei Funktionen pro Iteration erstellt. Erst die anonyme, dann die von.bind()
. Besser wäre es, es außerhalb der Schleife zu erstellen, dann.bind()
innerhalb.bind
es verwendet wird. Ich habe gemäß Ihren Vorschlägen ein weiteres Beispiel hinzugefügt.this
.Verwenden eines sofort aufgerufenen Funktionsausdrucks , der einfachste und am besten lesbare Weg, eine Indexvariable einzuschließen:
Dies sendet den Iterator
i
in die anonyme Funktion, als die wir definierenindex
. Dadurch wird ein Abschluss erstellt, in dem die Variablei
zur späteren Verwendung in einer asynchronen Funktionalität innerhalb des IIFE gespeichert wird.quelle
i
was ist, würde ich den Funktionsparameter in umbenennenindex
.index
anstelle von verwenden würdeni
.var funcs = {}; for (var i = 0; i < 3; i++) { funcs[i] = (function(index) { return function() {console.log('iterator: ' + index);}; })(i); }; for (var j = 0; j < 3; j++) { funcs[j](); }
.forEach()
Zeit, wenn man mit einem Array beginnt,forEach()
ist eine gute Wahl, wie:var nums [4, 6, 7]; var funcs = {}; nums.forEach(function (num, i) { funcs[i] = function () { console.log(num); }; });
Etwas spät zur Party, aber ich habe dieses Problem heute untersucht und festgestellt, dass viele der Antworten nicht vollständig darauf eingehen, wie Javascript Bereiche behandelt, worauf es im Wesentlichen ankommt.
Wie viele andere erwähnt haben, besteht das Problem darin, dass die innere Funktion auf dieselbe
i
Variable verweist . Warum erstellen wir nicht einfach bei jeder Iteration eine neue lokale Variable und haben stattdessen die innere Funktionsreferenz darauf?Genau wie zuvor, wo jede innere Funktion den zuletzt zugewiesenen Wert ausgegeben hat
i
, gibt jetzt jede innere Funktion nur den zuletzt zugewiesenen Wert ausilocal
. Aber sollte nicht jede Iteration ihre eigene habenilocal
?Es stellt sich heraus, das ist das Problem. Jede Iteration hat denselben Gültigkeitsbereich, sodass jede Iteration nach der ersten nur überschrieben wird
ilocal
. Von MDN :Zur Betonung wiederholt:
Wir können dies sehen, indem
ilocal
wir überprüfen, bevor wir es in jeder Iteration deklarieren:Genau deshalb ist dieser Fehler so schwierig. Obwohl Sie eine Variable neu deklarieren, gibt Javascript keinen Fehler aus und JSLint gibt nicht einmal eine Warnung aus. Dies ist auch der Grund, warum der beste Weg, dies zu lösen, darin besteht, Verschlüsse zu nutzen. Dies ist im Wesentlichen die Idee, dass innere Funktionen in Javascript Zugriff auf äußere Variablen haben, da innere Bereiche äußere Bereiche "einschließen".
Dies bedeutet auch, dass innere Funktionen äußere Variablen "festhalten" und am Leben erhalten, selbst wenn die äußere Funktion zurückkehrt. Um dies zu nutzen, erstellen und rufen wir eine Wrapper-Funktion auf, um lediglich einen neuen Bereich zu erstellen, den neuen Bereich zu deklarieren
ilocal
und eine innere Funktion zurückzugeben, die verwendetilocal
(weitere Erläuterungen unten):Durch das Erstellen der inneren Funktion innerhalb einer Wrapper-Funktion erhält die innere Funktion eine private Umgebung, auf die nur sie zugreifen kann, einen "Abschluss". Daher erstellen wir jedes Mal, wenn wir die Wrapper-Funktion aufrufen, eine neue innere Funktion mit einer eigenen separaten Umgebung, um sicherzustellen, dass die
ilocal
Variablen nicht kollidieren und sich gegenseitig überschreiben. Ein paar kleinere Optimierungen geben die endgültige Antwort, die viele andere SO-Benutzer gegeben haben:Aktualisieren
Mit ES6 als Mainstream können wir jetzt das neue
let
Schlüsselwort verwenden, um Variablen mit Blockbereich zu erstellen:Schau wie einfach es jetzt ist! Weitere Informationen finden Sie in dieser Antwort , auf der meine Informationen basieren.
quelle
let
undconst
. Wenn diese Antwort um diese erweitert würde, wäre sie meiner Meinung nach global nützlicher.let
und eine vollständigere Erklärungi=0; while(i < 100) { setTimeout(function(){ window.open("https://www.bbc.com","_self") }, 3000); setTimeout(function(){ window.open("https://www.cnn.com","_self") }, 3000); i++ }
? (könnte window.open () durch getelementbyid ersetzen ......)i
in Ihren Timeout-Funktionen, so dass Sie keinen Abschluss benötigenDa ES6 jetzt weitgehend unterstützt wird, hat sich die beste Antwort auf diese Frage geändert. ES6 bietet die Schlüsselwörter
let
undconst
für diesen genauen Umstand. Anstatt mit Abschlüssen herumzuspielen, können wir einfachlet
eine Schleifenbereichsvariable wie folgt festlegen:val
zeigt dann auf ein Objekt, das für diese bestimmte Umdrehung der Schleife spezifisch ist, und gibt den korrekten Wert ohne die zusätzliche Abschlussnotation zurück. Dies vereinfacht dieses Problem offensichtlich erheblich.const
ähneltlet
der zusätzlichen Einschränkung, dass der Variablenname nach der ersten Zuweisung nicht auf eine neue Referenz zurückgesetzt werden kann.Die Browserunterstützung ist jetzt für diejenigen verfügbar, die auf die neuesten Browserversionen abzielen.
const
/let
werden derzeit in den neuesten Versionen von Firefox, Safari, Edge und Chrome unterstützt. Es wird auch in Node unterstützt und Sie können es überall verwenden, indem Sie Build-Tools wie Babel nutzen. Ein funktionierendes Beispiel finden Sie hier: http://jsfiddle.net/ben336/rbU4t/2/Docs hier:
Beachten Sie jedoch, dass IE9-IE11 und Edge vor Edge 14 unterstützt werden,
let
aber die oben genannten Fehler auftreten (sie erstellen nichti
jedes Mal eine neue , sodass alle oben genannten Funktionen 3 protokollieren würden, wie wenn wir sie verwenden würdenvar
). Edge 14 macht es endlich richtig.quelle
Eine andere Art zu sagen ist, dass die
i
in Ihrer Funktion zum Zeitpunkt der Ausführung der Funktion gebunden ist, nicht zum Zeitpunkt der Erstellung der Funktion.Wenn Sie den Abschluss erstellen,
i
ist dies ein Verweis auf die im externen Bereich definierte Variable und keine Kopie davon, wie sie beim Erstellen des Abschlusses war . Es wird zum Zeitpunkt der Ausführung ausgewertet.Die meisten anderen Antworten bieten Möglichkeiten zur Umgehung, indem Sie eine weitere Variable erstellen, die den Wert für Sie nicht ändert.
Ich dachte nur, ich würde eine Erklärung für Klarheit hinzufügen. Für eine Lösung persönlich würde ich mich für Harto entscheiden, da dies die selbsterklärendste Methode ist, dies anhand der Antworten hier zu tun. Jeder der veröffentlichten Codes wird funktionieren, aber ich würde mich für eine Closure Factory entscheiden, weil ich einen Stapel Kommentare schreiben muss, um zu erklären, warum ich eine neue Variable deklariere (Freddy und 1800) oder eine seltsame eingebettete Closure-Syntax (Apphacker) habe.
quelle
Was Sie verstehen müssen, ist, dass der Umfang der Variablen in Javascript auf der Funktion basiert. Dies ist ein wichtiger Unterschied zu c #, wenn Sie einen Blockbereich haben, und das Kopieren der Variablen in eine Variable innerhalb des for funktioniert.
Das Einschließen in eine Funktion, die die Rückgabe der Funktion wie die Antwort des Apphackers auswertet, reicht aus, da die Variable jetzt den Funktionsumfang hat.
Es gibt auch ein let-Schlüsselwort anstelle von var, mit dem die Blockbereichsregel verwendet werden kann. In diesem Fall würde das Definieren einer Variablen innerhalb des for den Trick tun. Das Schlüsselwort let ist jedoch aus Kompatibilitätsgründen keine praktische Lösung.
quelle
Hier ist eine weitere Variation der Technik, ähnlich der von Björn (Apphacker), mit der Sie den Variablenwert innerhalb der Funktion zuweisen können, anstatt ihn als Parameter zu übergeben, was manchmal klarer sein kann:
Beachten Sie, dass die
index
Variable unabhängig von der verwendeten Technik zu einer Art statischer Variable wird, die an die zurückgegebene Kopie der inneren Funktion gebunden ist. Das heißt, Änderungen an seinem Wert bleiben zwischen den Aufrufen erhalten. Es kann sehr praktisch sein.quelle
var
Leitung und derreturn
Leitung würde nicht funktionieren? Vielen Dank!var
undreturn
die Variable dann nicht zugewiesen wurde, bevor sie die innere Funktion zurückgegeben hat.Dies beschreibt den häufigen Fehler bei der Verwendung von Verschlüssen in JavaScript.
Eine Funktion definiert eine neue Umgebung
Erwägen:
Bei jedem
makeCounter
Aufruf wird{counter: 0}
ein neues Objekt erstellt. Außerdem wird eine neue Kopie vonobj
erstellt, um auf das neue Objekt zu verweisen. Somitcounter1
undcounter2
sind unabhängig voneinander.Verschlüsse in Schleifen
Die Verwendung eines Verschlusses in einer Schleife ist schwierig.
Erwägen:
Beachten Sie, dass
counters[0]
undcounters[1]
sind nicht unabhängig. In der Tat arbeiten sie auf dem gleichenobj
!Dies liegt daran, dass es nur eine Kopie von gibt
obj
allen Iterationen der Schleife gemeinsam genutzt wird, möglicherweise aus Leistungsgründen. Obwohl{counter: 0}
in jeder Iteration ein neues Objekt erstellt wird, wird dieselbe Kopie vonobj
nur mit einem Verweis auf das neueste Objekt aktualisiert.Die Lösung besteht darin, eine andere Hilfsfunktion zu verwenden:
Dies funktioniert, weil lokalen Variablen im Funktionsumfang direkt sowie Funktionsargumentvariablen bei der Eingabe neue Kopien zugewiesen werden.
quelle
var obj = {counter: 0};
vor der Ausführung eines Codes Folgendes ausgewertet wird: MDN var : var-Deklarationen werden, wo immer sie auftreten, verarbeitet, bevor ein Code ausgeführt wird.Die einfachste Lösung wäre:
Anstatt zu verwenden:
was dreimal "2" warnt. Dies liegt daran, dass anonyme Funktionen, die in der for-Schleife erstellt wurden, denselben Abschluss haben und in diesem Abschluss der Wert von
i
gleich ist. Verwenden Sie dies, um ein gemeinsames Schließen zu verhindern:Die Idee dahinter ist, den gesamten Körper der for-Schleife mit einem IIFE (Sofort aufgerufener Funktionsausdruck) zu kapseln ,
new_i
als Parameter zu übergeben und als zu erfasseni
. Da die anonyme Funktion sofort ausgeführt wird, wird diei
Wert für jede in der anonymen Funktion definierte Funktion unterschiedlich.Diese Lösung scheint zu jedem solchen Problem zu passen, da nur minimale Änderungen am ursprünglichen Code erforderlich sind, der unter diesem Problem leidet. In der Tat ist dies beabsichtigt, es sollte überhaupt kein Problem sein!
quelle
versuchen Sie dieses kürzere
kein Array
kein extra für loop
http://jsfiddle.net/7P6EN/
quelle
Hier ist eine einfache Lösung, die verwendet
forEach
(funktioniert zurück zu IE9):Drucke:
quelle
Das Hauptproblem mit dem vom OP angezeigten Code ist, dass er
i
erst in der zweiten Schleife gelesen wird. Stellen Sie sich zur Demonstration einen Fehler im Code vorDer Fehler tritt tatsächlich erst auf, wenn er
funcs[someIndex]
ausgeführt wird()
. Unter Verwendung derselben Logik sollte es offensichtlich sein, dass der Wert voni
auch erst zu diesem Zeitpunkt erfasst wird. Sobald die ursprünglichen Schleife beendet ist ,i++
bringti
auf den Wert von3
dem in dem Zustand führti < 3
versagen und die Endung Schleife. An diesem Punkti
ist3
und so, wannfuncs[someIndex]()
verwendet wird, undi
ausgewertet wird, ist es 3 - jedes Mal.Um dies zu überwinden, müssen Sie bewerten,
i
wie es angetroffen wird. Beachten Sie, dass dies bereits in Form vonfuncs[i]
(wo es 3 eindeutige Indizes gibt) geschehen ist . Es gibt verschiedene Möglichkeiten, diesen Wert zu erfassen. Eine besteht darin, es als Parameter an eine Funktion zu übergeben, die hier bereits auf verschiedene Arten gezeigt wird.Eine andere Möglichkeit besteht darin, ein Funktionsobjekt zu erstellen, das über der Variablen geschlossen werden kann. Das kann so erreicht werden
jsFiddle Demo
quelle
JavaScript-Funktionen "schließen" den Bereich, auf den sie bei der Deklaration Zugriff haben, und behalten den Zugriff auf diesen Bereich bei, selbst wenn sich Variablen in diesem Bereich ändern.
Jede Funktion im obigen Array wird über den globalen Bereich geschlossen (global, einfach weil dies der Bereich ist, in dem sie deklariert sind).
Später werden diese Funktionen aufgerufen, um den aktuellsten Wert
i
im globalen Bereich zu protokollieren . Das ist die Magie und Frustration der Schließung."JavaScript-Funktionen schließen über den Bereich, in dem sie deklariert sind, und behalten den Zugriff auf diesen Bereich, auch wenn sich die Variablenwerte innerhalb dieses Bereichs ändern."
Verwenden Sie
let
statt zuvar
lösen, indem Sie bei jeder Ausführung derfor
Schleife einen neuen Bereich erstellen und einen separaten Bereich für jede Funktion erstellen, die geschlossen werden soll. Verschiedene andere Techniken machen dasselbe mit zusätzlichen Funktionen.(
let
macht Variablen blockumfangig. Blöcke werden durch geschweifte Klammern gekennzeichnet, aber im Fall der for-Schleife wird die Initialisierungsvariablei
in unserem Fall als in den Klammern deklariert betrachtet.)quelle
i
wird auf den globalen Bereich gesetzt. Wenn diefor
Schleife beendet ist, ist der globale Wert voni
jetzt 3. Wenn diese Funktion im Array aufgerufen wird (z. B. mitfuncs[j]
),i
verweist die Funktion in dieser Funktion auf die globalei
Variable (3).Nachdem ich verschiedene Lösungen durchgelesen habe, möchte ich hinzufügen, dass der Grund, warum diese Lösungen funktionieren, darin besteht, sich auf das Konzept der Scope-Kette zu stützen . Auf diese Weise löst JavaScript eine Variable während der Ausführung auf.
var
und seinen deklarierten lokalen Variablen bestehtarguments
.window
.Im ursprünglichen Code:
Wenn
funcs
ausgeführt wird, wird die Scope-Kette seinfunction inner -> global
. Da die Variablei
in nicht gefunden werden kannfunction inner
(weder mitvar
als deklariert noch als Argumente übergeben), wird die Suche fortgesetzt, bis der Wert voni
schließlich im globalen Bereich gefunden wirdwindow.i
.Indem Sie es in eine äußere Funktion einschließen, definieren Sie entweder explizit eine Hilfsfunktion wie Harto oder verwenden Sie eine anonyme Funktion wie Björn :
Wenn
funcs
ausgeführt wird, ist jetzt die Scope-Kettefunction inner -> function outer
. Diese Zeiti
befindet sich im Bereich der äußeren Funktion, der dreimal in der for-Schleife ausgeführt wird, wobei jedes Mal der Werti
korrekt gebunden ist. Es wird nicht der Wert verwendet,window.i
wenn inner ausgeführt wird.Weitere Details finden Sie hier.
Es enthält den häufigen Fehler beim Erstellen eines Abschlusses in der Schleife als das, was wir hier haben, sowie warum wir einen Abschluss benötigen und die Leistungsüberlegung.
quelle
Array.prototype.forEach(function callback(el) {})
natürlich funktionieren: Der Rückruf, der auf natürliche Weise übergeben wird, bildet den Umbruchbereich, wobei el in jeder Iteration von korrekt gebunden istforEach
. So kann jede im Rückruf definierte innere Funktion den richtigenel
Wert verwendenMit den neuen Funktionen von ES6 wird das Scoping auf Blockebene verwaltet:
Der Code in der Frage von OP wird durch
let
anstelle von ersetztvar
.quelle
const
liefert das gleiche Ergebnis und sollte verwendet werden, wenn sich der Wert einer Variablen nicht ändert. Die Verwendungconst
innerhalb des Initialisierers der for-Schleife ist in Firefox jedoch falsch implementiert und muss noch behoben werden. Anstatt innerhalb des Blocks deklariert zu werden, wird es außerhalb des Blocks deklariert, was zu einer erneuten Deklaration der Variablen führt, was wiederum zu einem Fehler führt. Die Verwendung deslet
Initialisierers ist in Firefox korrekt implementiert, sodass Sie sich dort keine Sorgen machen müssen.Ich bin überrascht, dass noch niemand vorgeschlagen hat, die
forEach
Funktion zu verwenden, um die (lokale) Verwendung lokaler Variablen besser zu vermeiden. Tatsächlich benutze ichfor(var i ...)
aus diesem Grund überhaupt nicht mehr.// bearbeitet, um
forEach
anstelle der Karte zu verwenden.quelle
.forEach()
ist eine viel bessere Option, wenn Sie eigentlich nichts zuordnen, und Daryl schlug dies 7 Monate vor dem Posten vor, sodass Sie sich nicht wundern sollten.Diese Frage zeigt wirklich die Geschichte von JavaScript! Jetzt können wir das Block-Scoping mit Pfeilfunktionen vermeiden und Schleifen direkt von DOM-Knoten mithilfe von Object-Methoden verarbeiten.
quelle
Der Grund, warum Ihr ursprüngliches Beispiel nicht funktioniert hat, ist, dass alle in der Schleife erstellten Abschlüsse auf denselben Frame verweisen. In der Tat 3 Methoden für ein Objekt mit nur einer einzigen
i
Variablen. Sie alle druckten den gleichen Wert aus.quelle
Verstehen Sie zunächst, was mit diesem Code nicht stimmt:
Hier wird, wenn das
funcs[]
Array initialisierti
wird, inkrementiert, dasfuncs
Array initialisiert und die Größe desfunc
Arrays wird 3, alsoi = 3,
. Wenn dasfuncs[j]()
aufgerufen wird, verwendet es wieder die Variablei
, die bereits auf 3 erhöht wurde.Um dies zu lösen, haben wir viele Möglichkeiten. Unten sind zwei davon:
Wir können initialisieren
i
mitlet
oder eine neue Variable initialisierenindex
mitlet
und machen es gleich zui
. Wenn der Anruf getätigt wird,index
wird er verwendet und sein Gültigkeitsbereich endet nach der Initialisierung. Und für den Anrufindex
wird erneut initialisiert:Eine andere Option kann darin bestehen, eine einzuführen,
tempFunc
die die eigentliche Funktion zurückgibt:quelle
Verwenden Sie eine Verschlussstruktur , dies würde Ihre zusätzliche for-Schleife reduzieren. Sie können dies in einer einzigen for-Schleife tun:
quelle
Fall 1 : Verwenden
var
Öffnen Sie nun Ihr Chrome-Konsolenfenster, indem Sie F12 drücken und die Seite aktualisieren. Erweitern Sie alle 3 Funktionen innerhalb des Arrays. Sie sehen eine Eigenschaft namens
[[Scopes]]
.Expand diese. Sie sehen ein Array-Objekt namens"Global"
, erweitern Sie dieses. Sie finden eine im Objekt'i'
deklarierte Eigenschaft mit dem Wert 3.Fazit:
'var'
außerhalb einer Funktion deklarieren , wird sie zu einer globalen Variablen (Sie können dies durch Eingabei
oderwindow.i
im Konsolenfenster überprüfen. Es wird 3 zurückgegeben).console.log("My value: " + i)
nehmen Sie den Wert aus ihremGlobal
Objekt und zeigen Sie das Ergebnis an.FALL2: mit let
Ersetzen Sie nun die
'var'
durch'let'
Mach dasselbe, geh in die Zielfernrohre. Jetzt sehen Sie zwei Objekte
"Block"
und"Global"
. ErweiternBlock
Sie nun das Objekt. Sie werden sehen, dass dort 'i' definiert ist, und das Seltsame ist, dass für jede Funktion der Wert ifi
unterschiedlich ist (0, 1, 2).Fazit:
Wenn Sie eine Variable
'let'
auch außerhalb der Funktion, aber innerhalb der Schleife deklarieren , ist diese Variable keine globale Variable, sondern eineBlock
Ebenenvariable, die nur für dieselbe Funktion verfügbar ist. Aus diesem Grund erhalten wir den Werti
different für jede Funktion, wenn wir die Funktionen aufrufen.Weitere Informationen darüber, wie näher funktioniert, finden Sie im fantastischen Video-Tutorial https://youtu.be/71AtaJpJHw0
quelle
Sie können ein deklaratives Modul für Datenlisten wie query-js (*) verwenden. In diesen Situationen finde ich persönlich einen deklarativen Ansatz weniger überraschend
Sie könnten dann Ihre zweite Schleife verwenden und das erwartete Ergebnis erhalten, oder Sie könnten es tun
(*) Ich bin der Autor von query-js und daher voreingenommen, es zu verwenden. Nehmen Sie meine Worte also nicht als Empfehlung für diese Bibliothek, nur für den deklarativen Ansatz :)
quelle
Query.range(0,3)
? Dies ist nicht Teil der Tags für diese Frage. Wenn Sie eine Bibliothek eines Drittanbieters verwenden, können Sie außerdem den Link zur Dokumentation bereitstellen.Ich bevorzuge die Verwendung einer
forEach
Funktion, die einen eigenen Abschluss beim Erstellen eines Pseudobereichs hat:Das sieht hässlicher aus als Bereiche in anderen Sprachen, aber meiner Meinung nach weniger monströs als andere Lösungen.
quelle
Und noch eine andere Lösung: Anstatt eine weitere Schleife zu erstellen, binden Sie die einfach
this
an die Rückgabefunktion.Durch die Bindung dieser , löst das Problem auch.
quelle
Viele Lösungen scheinen korrekt zu sein, erwähnen jedoch nicht, dass es sich
Currying
um ein funktionales Programmierentwurfsmuster für Situationen wie hier handelt. Je nach Browser 3-10 mal schneller als Binden.Sehen Sie den Leistungszuwachs in verschiedenen Browsern .
quelle
Ihr Code funktioniert nicht, weil er Folgendes tut:
Die Frage ist nun, was ist der Wert der Variablen,
i
wenn die Funktion aufgerufen wird? Da die erste Schleife mit der Bedingung von erstellt wirdi < 3
, stoppt sie sofort, wenn die Bedingung falsch isti = 3
.Sie müssen verstehen, dass zum Zeitpunkt der Erstellung Ihrer Funktionen kein Code ausgeführt wird, sondern nur für später gespeichert wird. Wenn sie später aufgerufen werden, führt der Dolmetscher sie aus und fragt: "Was ist der aktuelle Wert von
i
?"Ihr Ziel ist es also, zuerst den Wert von
i
to function und erst danach die Funktion to zu speichernfuncs
. Dies könnte zum Beispiel folgendermaßen geschehen:Auf diese Weise hat jede Funktion ihre eigene Variable
x
und wir setzen diese in jeder Iterationx
auf den Wert voni
.Dies ist nur eine von mehreren Möglichkeiten, um dieses Problem zu lösen.
quelle
quelle
Verwenden Sie let (Blocked-Scope) anstelle von var.
quelle