Mit diesem Code:
function baz() {
var x = "foo";
function bar() {
debugger;
};
bar();
}
baz();
Ich bekomme dieses unerwartete Ergebnis:
Wenn ich den Code ändere:
function baz() {
var x = "foo";
function bar() {
x;
debugger;
};
bar();
}
Ich bekomme das erwartete Ergebnis:
Wenn eval
innerhalb der inneren Funktion ein Aufruf erfolgt , kann ich auf meine Variable zugreifen, wie ich möchte (unabhängig davon, an was ich übergebe eval
).
In der Zwischenzeit geben die Firefox-Entwicklungstools unter beiden Umständen das erwartete Verhalten wieder.
Was ist mit Chrome los, dass sich der Debugger weniger bequem verhält als Firefox? Ich habe dieses Verhalten seit einiger Zeit bis einschließlich Version 41.0.2272.43 Beta (64-Bit) beobachtet.
Ist es so, dass die Javascript-Engine von Chrome die Funktionen "abflacht", wenn dies möglich ist?
Interessanterweise ist die x
Variable immer noch undefiniert, wenn ich eine zweite Variable hinzufüge, auf die in der inneren Funktion verwiesen wird.
Ich verstehe, dass es bei der Verwendung eines interaktiven Debuggers häufig Macken mit Umfang und Variablendefinition gibt, aber es scheint mir, dass es basierend auf der Sprachspezifikation eine "beste" Lösung für diese Macken geben sollte. Ich bin also sehr gespannt, ob dies daran liegt, dass Chrome weiter optimiert als Firefox. Und auch, ob diese Optimierungen während der Entwicklung leicht deaktiviert werden können oder nicht (sollten sie möglicherweise deaktiviert werden, wenn Entwickler-Tools geöffnet sind?).
Ich kann dies auch mit Haltepunkten sowie der debugger
Anweisung reproduzieren .
quelle
debugger;
Leitung nicht von innen aufgerufen wirdbar
. Schauen Sie sich also den Stack-Trace an, wenn er im Debugger angehalten wird: Wird diebar
Funktion im Stacktrace erwähnt? Wenn ich recht habe, sollte der Stacktrace sagen, dass er in Zeile 5, in Zeile 7, in Zeile 9 angehalten ist.temp1
wird an die Konsole angehängt und Sie können damit auf den Bereichseintrag zugreifen.Antworten:
Ich habe einen v8-Problembericht gefunden , der genau das beschreibt, was Sie fragen.
Um zusammenzufassen, was in diesem Problembericht gesagt wird ... v8 kann die Variablen, die für eine Funktion lokal sind, auf dem Stapel oder in einem "Kontext" -Objekt speichern, das sich auf dem Heap befindet. Es werden lokale Variablen auf dem Stapel zugewiesen, solange die Funktion keine innere Funktion enthält, die auf sie verweist. Es ist eine Optimierung . Wenn sich eine innere Funktion auf eine lokale Variable bezieht, wird diese Variable in ein Kontextobjekt eingefügt (dh auf den Heap anstatt auf den Stapel). Der Fall von
eval
ist etwas Besonderes: Wenn er überhaupt von einer inneren Funktion aufgerufen wird, werden alle lokalen Variablen in das Kontextobjekt eingefügt.Der Grund für das Kontextobjekt ist, dass Sie im Allgemeinen eine innere Funktion von der äußeren zurückgeben können und dann der Stapel, der während der Ausführung der äußeren Funktion vorhanden war, nicht mehr verfügbar ist. Alles, worauf die innere Funktion zugreift, muss die äußere Funktion überleben und auf dem Haufen und nicht auf dem Stapel leben.
Der Debugger kann die auf dem Stapel befindlichen Variablen nicht überprüfen. In Bezug auf das Problem beim Debuggen sagt ein Projektmitglied :
Hier ist ein Beispiel für "Wenn sich eine innere Funktion auf die Variable bezieht, fügen Sie sie in ein Kontextobjekt ein". Wenn Sie dies ausführen, können Sie
x
auf diedebugger
Anweisung zugreifen , obwohl siex
nur in derfoo
Funktion verwendet wird, die niemals aufgerufen wird !quelle
Wie @Louis sagte, wurde es durch v8-Optimierungen verursacht. Sie können den Aufrufstapel zu einem Frame durchlaufen, in dem diese Variable sichtbar ist:
Oder ersetzen
debugger
durcheval
wird den aktuellen Block deaktivierenquelle
debugger
, und der Kontext ist tatsächlich verfügbar. Wenn Sie den Stapel um eine Ebene auf den Code erhöhen, den Sie tatsächlich debuggen möchten, haben Sie wieder keinen Zugriff auf den Kontext. Es ist also nur ein bisschen klobig, nicht in der Lage zu sein, den Code, den Sie debuggen, beim Zugriff auf versteckte Schließungsvariablen anzuzeigen. Ich werde mich jedoch dafür entscheiden, da ich keinen Code hinzufügen muss, der nicht offensichtlich zum Debuggen geeignet ist, und auf den gesamten Kontext zugreifen kann, ohne die gesamte App zu deoptimieren.eval
um Zugriff auf den Kontext zu erhalten: Sie können den Code nicht schrittweise durchlaufen (es sei denn, Sie setzeneval('debugger')
zwischen alle Zeilen, die Sie durchlaufen möchten).controllers.forEach(c => c.update())
und habe irgendwo tief drinnen einen Haltepunkt erreichtc.update()
. Wenn ich dann den Frame auswähle, in demcontrollers.forEach()
aufgerufen wird,controllers
ist er undefiniert (aber alles andere in diesem Frame ist sichtbar). Ich konnte nicht mit einer minimalen Version reproduzieren, ich nehme an, dass es eine Komplexitätsschwelle gibt, die überschritten werden muss oder so.somewhere deep inside c.update()
Ihr Code wird asynchron und Sie sehenIch habe dies auch in nodejs bemerkt. Ich glaube (und ich gebe zu, dass dies nur eine Vermutung ist), dass der Code, wenn er kompiliert wird, wenn er
x
nicht im Inneren erscheintbar
, nichtx
im Rahmen von verfügbar gemacht wirdbar
. Dies macht es wahrscheinlich etwas effizienter; das Problem ist , jemand vergessen (oder Pflege nicht) , dass selbst wenn es keine istx
inbar
, der Debugger laufen und damit noch brauchen , um Zugang können entscheiden , obx
von innenbar
.quelle
eval
Befehl gibt. Wenn die Variable deklariert ist, sollte sie zugänglich sein.Wow, wirklich interessant!
Wie andere erwähnt haben, scheint dies damit zu tun zu haben
scope
, aber insbesondere damitdebugger scope
. Wenn injiziertes Skript in den Entwicklertools ausgewertet wird, scheint es a zu bestimmenScopeChain
, was zu einer gewissen Eigenart führt (da es an den Inspector / Debugger-Bereich gebunden ist). Eine Variation von dem, was Sie gepostet haben, ist folgende:(BEARBEITEN - eigentlich erwähnen Sie dies in Ihrer ursprünglichen Frage, yikes, mein schlechtes! )
Für die Ehrgeizigen und / oder Neugierigen (heh) suchen Sie die Quelle heraus, um zu sehen, was los ist:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger
quelle
Ich vermute, dass dies mit dem Heben von Variablen und Funktionen zu tun hat. JavaScript bringt alle Variablen- und Funktionsdeklarationen an den Anfang der Funktion, in der sie definiert sind. Weitere Informationen finden Sie hier: http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/
Ich wette, Chrome ruft den Haltepunkt mit der Variablen auf, die für den Bereich nicht verfügbar ist, da die Funktion nichts anderes enthält. Das scheint zu funktionieren:
Wie das:
Hoffe das und / oder der obige Link hilft. Dies sind meine Lieblings-SO-Fragen, übrigens :)
quelle