Za pomocą tego kodu:
function baz() {
var x = "foo";
function bar() {
debugger;
};
bar();
}
baz();
Otrzymuję ten nieoczekiwany wynik:
Kiedy zmieniam kod:
function baz() {
var x = "foo";
function bar() {
x;
debugger;
};
bar();
}
Otrzymuję oczekiwany wynik:
Ponadto, jeśli jest jakieś wywołanie eval
w funkcji wewnętrznej, mogę uzyskać dostęp do mojej zmiennej tak, jak chcę (nie ma znaczenia, do czego przekażę eval
).
W międzyczasie narzędzia deweloperskie Firefoksa zapewniają oczekiwane zachowanie w obu przypadkach.
Co jest z Chrome, że debugger zachowuje się mniej wygodnie niż Firefox? Obserwowałem to zachowanie od jakiegoś czasu, aż do wersji beta 41.0.2272.43 włącznie (wersja 64-bitowa).
Czy to dlatego, że silnik javascript Chrome „spłaszcza” funkcje, kiedy jest to możliwe?
Co ciekawe, jeśli dodam drugą zmienną, która jest odwołanie w funkcji wewnętrznej,x
zmienna jest nadal nieokreślone.
Rozumiem, że podczas korzystania z interaktywnego debuggera często pojawiają się dziwactwa związane z zakresem i definicją zmiennych, ale wydaje mi się, że w oparciu o specyfikację języka powinno być „najlepsze” rozwiązanie tych dziwactw. Jestem więc bardzo ciekawy, czy wynika to z dalszej optymalizacji Chrome niż Firefox. A także, czy te optymalizacje można łatwo wyłączyć podczas programowania (może powinny być wyłączone, gdy narzędzia deweloperskie są otwarte?).
Mogę również odtworzyć to z punktami przerwania, a także z debugger
instrukcją.
źródło
debugger;
linia nie jest wywoływana od wewnątrzbar
. Spójrz więc na ślad stosu, gdy zatrzymuje się w debugerze: Czybar
funkcja jest wymieniona w stosie śledzenia? Jeśli mam rację, to ślad stosu powinien wskazywać, że jest wstrzymany w linii 5, w linii 7, w linii 9.temp1
Do konsoli dołączona jest nowa zmienna globalna, której można użyć, aby uzyskać dostęp do wpisu zakresu.Odpowiedzi:
Znalazłem raport o problemie z wersją 8, który dokładnie dotyczy tego, o co pytasz.
Teraz, podsumowując to, co zostało powiedziane w tym raporcie o problemie ... v8 może przechowywać zmienne, które są lokalne dla funkcji na stosie lub w obiekcie „kontekstu”, który żyje na stercie. Przydzieli zmienne lokalne na stosie, o ile funkcja nie zawiera żadnej funkcji wewnętrznej, która się do nich odwołuje. To jest optymalizacja . Jeśli jakakolwiek funkcja wewnętrzna odwołuje się do zmiennej lokalnej, zmienna ta zostanie umieszczona w obiekcie kontekstu (tj. Na stercie zamiast na stosie). Przypadek
eval
jest szczególny: jeśli w ogóle jest wywoływana przez funkcję wewnętrzną, wszystkie zmienne lokalne są umieszczane w obiekcie kontekstu.Przyczyną istnienia obiektu kontekstu jest to, że generalnie można zwrócić funkcję wewnętrzną z funkcji zewnętrznej, a wtedy stos, który istniał, podczas gdy uruchomiona funkcja zewnętrzna, nie będzie już dostępny. Zatem wszystko, do czego funkcja wewnętrzna uzyskuje dostęp, musi przetrwać funkcję zewnętrzną i żyć na stercie, a nie na stosie.
Debuger nie może sprawdzić tych zmiennych, które znajdują się na stosie. Odnośnie problemu napotkanego podczas debugowania, jeden z członków projektu mówi :
Oto przykład „jeśli jakakolwiek funkcja wewnętrzna odwołuje się do zmiennej, umieść ją w obiekcie kontekstu”. Jeśli to uruchomisz, będziesz mieć dostęp
x
dodebugger
instrukcji, nawet jeślix
jest ona używana tylko wfoo
funkcji, która nigdy nie jest wywoływana !źródło
Jak @Louis powiedział, że jest to spowodowane optymalizacjami w wersji 8. Możesz przechodzić ze stosu wywołań do ramki, w której ta zmienna jest widoczna:
Lub wymienić
debugger
zeval
deoptuje bieżący fragmentźródło
debugger
i kontekst jest rzeczywiście dostępny. Jeśli zwiększysz stos o jeden poziom do kodu, który faktycznie próbujesz debugować, wrócisz do braku dostępu do kontekstu. Jest to więc po prostu trochę niezgrabne, ponieważ nie można spojrzeć na debugowany kod podczas uzyskiwania dostępu do ukrytych zmiennych zamknięcia. Głosuję jednak za, ponieważ oszczędza mi to konieczności dodawania kodu, który nie jest oczywiście do debugowania, i daje mi dostęp do całego kontekstu bez dezoptymalizacji całej aplikacji.eval
okna źródłowego ed, aby uzyskać dostęp do kontekstu: nie możesz przechodzić przez kod (chyba że umieściszeval('debugger')
między wszystkimi liniami, przez które chcesz przejść).controllers.forEach(c => c.update())
i trafiłem gdzieś głęboko w breakpointc.update()
. Jeśli następnie zaznaczę ramkę, w którejcontrollers.forEach()
jest wywołana,controllers
jest niezdefiniowana (ale wszystko inne w tej ramce jest widoczne). Nie mogłem odtworzyć w wersji minimalnej, przypuszczam, że może być jakiś próg złożoności, który należy przejść lub coś w tym stylu.somewhere deep inside c.update()
Twój kod przechodzi w stan asynchroniczny i widzisz ramkę stosu asynchronicznegoZauważyłem to również w nodejs. Uważam (i przyznaję, że to tylko przypuszczenie), że gdy kod jest kompilowany, jeśli
x
nie pojawia się w środkubar
, nie jestx
dostępny w zakresiebar
. To prawdopodobnie czyni go nieco bardziej wydajnym; problem jest ktoś zapomniał (lub nie obchodzi), że nawet jeśli nie znajduje sięx
wbar
, można zdecydować, aby uruchomić debugger, a więc nadal mieć dostępx
od wewnątrzbar
.źródło
eval
polecenie. Jeśli zmienna jest zadeklarowana, powinna być dostępna.Wow, naprawdę interesujące!
Jak wspominali inni, wydaje się, że jest to związane
scope
, ale dokładniej, związane zdebugger scope
. Kiedy wstrzykiwany skrypt jest oceniany w narzędziach programistycznych, wydaje się, że określa plikScopeChain
, co powoduje pewne dziwactwa (ponieważ jest powiązany z zakresem inspektora / debugera). Oto odmiana tego, co opublikowałeś:(EDYTUJ - właściwie wspomniałeś o tym w swoim pierwotnym pytaniu , o mój Boże! )
Dla ambitnych i / lub ciekawskich, sprawdź (heh) źródło, aby zobaczyć, co się dzieje:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger
źródło
Podejrzewam, że ma to związek z podnoszeniem zmiennym i funkcyjnym. JavaScript przenosi wszystkie deklaracje zmiennych i funkcji na początek funkcji, w której są zdefiniowane. Więcej informacji tutaj: http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/
Założę się, że Chrome wywołuje punkt przerwania ze zmienną niedostępną dla zakresu, ponieważ w funkcji nie ma nic innego. To wydaje się działać:
Jak to robi:
Mam nadzieję, że to i / lub powyższy link pomoże. To są moje ulubione rodzaje pytań SO, przy okazji :)
źródło