С этим кодом:
function baz() {
var x = "foo";
function bar() {
debugger;
};
bar();
}
baz();
Я получаю этот неожиданный результат:
Когда я меняю код:
function baz() {
var x = "foo";
function bar() {
x;
debugger;
};
bar();
}
Я получаю ожидаемый результат:
Кроме того, если есть какой-либо вызов eval
внутри внутренней функции, я могу получить доступ к своей переменной, как я хочу (не важно, что я передаю eval
).
Между тем, инструменты разработки Firefox дают ожидаемое поведение в обоих случаях.
Что случилось с Chrome, что отладчик ведет себя менее удобно, чем Firefox? Я наблюдал такое поведение в течение некоторого времени, вплоть до бета-версии 41.0.2272.43 (64-разрядная версия).
Это то, что движок Chrome javascript «выравнивает» функции, когда это возможно?
Интересно , если я добавляю вторую переменную , которая будет ссылаться во внутренней функции, тоx
переменная еще не определена.
Я понимаю, что при использовании интерактивного отладчика часто встречаются причуды с определением области действия и переменных, но мне кажется, что на основе спецификации языка должно быть «лучшее» решение этих причуд. Поэтому мне очень любопытно, связано ли это с оптимизацией Chrome дальше, чем с Firefox. А также, могут ли эти оптимизации быть легко отключены во время разработки (возможно, их следует отключить, когда инструменты разработки открыты?).
Кроме того, я могу воспроизвести это с точками останова, а также с debugger
заявлением.
источник
debugger;
линия на самом деле не вызывается изнутриbar
. Итак, посмотрите на трассировку стека, когда она останавливается в отладчике:bar
упоминается ли функция в трассировке стека? Если я прав, то трассировка стека должна сказать, что она приостановлена в строке 5, в строке 7, в строке 9.temp1
присоединена к консоли, и вы можете использовать ее для доступа к записи области.Ответы:
Я нашел отчет о проблеме v8, который точно о том, что вы спрашиваете.
Теперь, чтобы подвести итог сказанному в отчете о проблеме ... v8 может хранить переменные, которые являются локальными для функции в стеке или в объекте "context", который живет в куче. Он будет размещать локальные переменные в стеке, пока функция не содержит внутренней функции, которая на них ссылается. Это оптимизация . Если какая-либо внутренняя функция ссылается на локальную переменную, эта переменная будет помещена в объект контекста (т.е. в кучу, а не в стек). Случай
eval
особенный: если он вызывается внутренней функцией, все локальные переменные помещаются в объект контекста.Причина для объекта контекста в том, что в общем случае вы можете вернуть внутреннюю функцию из внешней, и тогда стек, который существовал во время выполнения внешней функции, больше не будет доступен. Поэтому все, к чему обращается внутренняя функция, должно пережить внешнюю функцию и жить в куче, а не в стеке.
Отладчик не может проверить те переменные, которые находятся в стеке. Что касается проблемы, возникающей при отладке, один из участников проекта говорит :
Вот пример «если какая-либо внутренняя функция ссылается на переменную, поместите ее в объект контекста». Если вы запустите это, вы сможете получить доступ
x
кdebugger
оператору, даже еслиx
он используется только вfoo
функции, которая никогда не вызывается !источник
Как сказал @Louis, это вызвано оптимизацией v8. Вы можете пройти стек вызовов к кадру, где видна эта переменная:
Или заменить
debugger
наeval
откажется от текущего чанкаисточник
debugger
, и контекст действительно доступен. Если вы поднимаетесь на один уровень вверх по стеку к коду, который на самом деле пытаетесь отлаживать, вы возвращаетесь к отсутствию доступа к контексту. Так что это немного неуклюже, потому что вы не можете смотреть на код, который вы отлаживаете, при доступе к скрытым переменным замыкания. Тем не менее, я буду одобрять, поскольку это избавляет меня от необходимости добавлять код, который явно не предназначен для отладки, и дает мне доступ ко всему контексту без деоптимизации всего приложения.eval
окна исходного текста для получения доступа к контексту: вы не можете пошагово проходить код (если не вставляетеeval('debugger')
между всеми строками, которые хотите пройти.)controllers.forEach(c => c.update())
и я достиг точки останова где-то глубоко внутриc.update()
. Если я затем выберу кадр, где онcontrollers.forEach()
вызывается,controllers
он не определен (но все остальное в этом кадре видно). Я не мог воспроизвести с минимальной версией, я предполагаю, что может быть некоторый порог сложности, который должен быть пройден, или что-то в этом роде.somewhere deep inside c.update()
ваш код работает асинхронно и вы видите кадр асинхронного стекаЯ также заметил это в nodejs. Я полагаю (и я признаю, что это только предположение), что когда код компилируется, если
x
не появляется внутриbar
, он не становитсяx
доступным внутри области действияbar
. Это, вероятно, делает его немного более эффективным; проблема в том , кто забыл (или не волнует) , что даже если там нетx
вbar
, вы не могли бы решить , чтобы запустить отладчик и , следовательно , по- прежнему необходимо получить доступx
из внутреннейbar
.источник
eval
команда. Если переменная объявлена, она должна быть доступна.Вау, действительно интересно!
Как уже упоминали другие, это, похоже, связано
scope
, но более конкретно, связано сdebugger scope
. Когда внедренный скрипт оценивается в инструментах разработчика, он, кажется, определяетScopeChain
, что приводит к некоторой причуде (так как он связан с областью инспектора / отладчика). Вариант того, что вы опубликовали, таков:(РЕДАКТИРОВАТЬ - на самом деле, вы упоминаете об этом в своем первоначальном вопросе, да , мой плохой! )
Для амбициозных и / или любопытных, охват (хех) источника, чтобы увидеть, что происходит:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger
источник
Я подозреваю, что это связано с перемещением переменных и функций. JavaScript переносит все объявления переменных и функций в начало функции, в которой они определены. Подробнее здесь: http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/
Могу поспорить, что Chrome вызывает точку останова с переменной, недоступной для области, потому что в функции больше ничего нет. Это похоже на работу:
Как это сделать:
Надеюсь, что и / или ссылка выше помогает. Это мой любимый вид ТАК вопросов, кстати :)
источник