Con este código:
function baz() {
var x = "foo";
function bar() {
debugger;
};
bar();
}
baz();
Me sale este resultado inesperado:
Cuando cambio el código:
function baz() {
var x = "foo";
function bar() {
x;
debugger;
};
bar();
}
Obtengo el resultado esperado:
Además, si hay alguna llamada a eval
la función interna, puedo acceder a mi variable como quiero hacerlo (no importa a lo que le pase eval
).
Mientras tanto, las herramientas de desarrollo de Firefox ofrecen el comportamiento esperado en ambas circunstancias.
¿Qué pasa con Chrome que el depurador se comporta de manera menos conveniente que Firefox? He observado este comportamiento durante algún tiempo, hasta la versión 41.0.2272.43 beta incluida (64 bits).
¿Es que el motor de JavaScript de Chrome "aplana" las funciones cuando puede?
Curiosamente, si agrego una segunda variable a la que se hace referencia en la función interna, la x
variable aún no está definida.
Entiendo que a menudo hay peculiaridades con el alcance y la definición de variables cuando se usa un depurador interactivo, pero me parece que, según la especificación del lenguaje, debería haber una "mejor" solución para estas peculiaridades. Así que tengo mucha curiosidad si esto se debe a la optimización de Chrome más allá de Firefox. Y también si estas optimizaciones pueden deshabilitarse fácilmente durante el desarrollo (¿tal vez deberían deshabilitarse cuando las herramientas de desarrollo están abiertas?).
Además, puedo reproducir esto con puntos de interrupción, así como la debugger
declaración.
fuente
debugger;
línea en realidad no se llama desde adentrobar
. Observe el seguimiento de la pila cuando se detiene en el depurador: ¿sebar
menciona la función en el seguimiento de la pila? Si estoy en lo cierto, entonces el stacktrace debería decir que está en pausa en la línea 5, en la línea 7, en la línea 9.temp1
adjunta una nueva variable global a la consola y puede usarla para acceder a la entrada del alcance.Respuestas:
He encontrado un informe de problema de v8 que trata precisamente de lo que estás preguntando.
Ahora, para resumir lo que se dice en ese informe de problema ... v8 puede almacenar las variables que son locales para una función en la pila o en un objeto de "contexto" que vive en el montón. Asignará variables locales en la pila siempre que la función no contenga ninguna función interna que se refiera a ellas. Es una optimización . Si alguna función interna se refiere a una variable local, esta variable se colocará en un objeto de contexto (es decir, en el montón en lugar de en la pila). El caso de
eval
es especial: si es invocado por una función interna, todas las variables locales se colocan en el objeto de contexto.La razón del objeto de contexto es que, en general, podría devolver una función interna de la externa y luego la pila que existía mientras se ejecutaba la función externa ya no estará disponible. Entonces, cualquier cosa a la que acceda la función interna debe sobrevivir a la función externa y vivir en el montón en lugar de en la pila.
El depurador no puede inspeccionar aquellas variables que están en la pila. Con respecto al problema encontrado en la depuración, un miembro del proyecto dice :
Aquí hay un ejemplo de "si alguna función interna se refiere a la variable, póngala en un objeto de contexto". Si ejecuta esto, podrá acceder
x
a ladebugger
instrucción aunquex
solo se use en lafoo
función, que nunca se llama .fuente
Como @Louis dijo que fue causado por las optimizaciones v8. Puede recorrer la pila de llamadas para enmarcar donde esta variable es visible:
O reemplazar
debugger
coneval
va a desarmar la porción actualfuente
debugger
, y el contexto está disponible. Si sube la pila un nivel al código que realmente está intentando depurar, volverá a no tener acceso al contexto. Por lo tanto, es un poco torpe, no poder ver el código que está depurando al acceder a las variables de cierre ocultas. Sin embargo, votaré, ya que me evita tener que agregar código que obviamente no es para depurar, y me da acceso a todo el contexto sin desoptimizar toda la aplicación.eval
ventana de origen ed amarilla para obtener acceso al contexto: no puedes pasar por el código (a menos que pongaseval('debugger')
entre todas las líneas por las que quieres pasar).controllers.forEach(c => c.update())
y llegué a un punto de interrupción en algún lugar muy profundoc.update()
. Si luego selecciono el marco dondecontrollers.forEach()
se llama,controllers
no está definido (pero todo lo demás en ese marco es visible). No pude reproducir con una versión mínima, supongo que puede haber algún umbral de complejidad que deba pasarse o algo así.somewhere deep inside c.update()
su código se vuelve asíncrono y ve un marco de pila asíncronoTambién he notado esto en nodejs. Creo (y admito que esto es solo una suposición) que cuando se compila el código, si
x
no aparece dentrobar
, no estáx
disponible dentro del alcance debar
. Esto probablemente lo hace un poco más eficiente; el problema es que alguien se olvidó (o no le importaba) que incluso si no hay esx
enbar
, puede decidir ejecutar el depurador y, por tanto, aún es necesario el accesox
desde el interiorbar
.fuente
eval
comando no relacionado . Si se declara la variable, debería ser accesible.Wow, muy interesante!
Como otros han mencionado, esto parece estar relacionado con
scope
, pero más específicamente, relacionado condebugger scope
. Cuando la secuencia de comandos inyectada se evalúa en las herramientas del desarrollador, parece determinar aScopeChain
, lo que resulta en una peculiaridad (ya que está vinculado al alcance del inspector / depurador). Una variación de lo que publicaste es esta:(EDITAR: en realidad, mencionas esto en tu pregunta original, ¡ ay, qué mal! )
Para los ambiciosos y / o curiosos, busque (heh) la fuente para ver qué sucede:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger
fuente
Sospecho que esto tiene que ver con la elevación de variables y funciones. JavaScript lleva todas las declaraciones de variables y funciones a la parte superior de la función en la que se definen. Más información aquí: http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/
Apuesto a que Chrome está llamando al punto de ruptura con la variable no disponible para el alcance porque no hay nada más en la función. Esto parece funcionar:
Como hace esto:
Espero esto, y / o el enlace de arriba ayuda. Estas son mi tipo favorito de preguntas SO, por cierto :)
fuente