¿Por qué el depurador de Chrome cree que la variable local cerrada no está definida?

Resuelto Gabe Kopley asked hace 9 años • 6 respuestas

Con este código:

function baz() {
  var x = "foo";

  function bar() {
    debugger;
  };
  bar();
}
baz();

Obtengo este resultado inesperado:

ingrese la descripción de la imagen aquí

Cuando cambio el código:

function baz() {
  var x = "foo";

  function bar() {
    x;
    debugger;
  };
  bar();
}

Obtengo el resultado esperado:

ingrese la descripción de la imagen aquí

Además, si hay alguna llamada evaldentro de la función interna, puedo acceder a mi variable como quiero (no importa a qué le pase eval).

Mientras tanto, las herramientas de desarrollo de Firefox dan 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 (64 bits) incluida.

¿Será que el motor 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 xvariable aún no está definida.

Entiendo que a menudo hay peculiaridades con el alcance y la definición de variables cuando se utiliza 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 que Chrome se optimiza más que Firefox. Y también si estas optimizaciones se pueden deshabilitar 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 además de la debuggerdeclaración.

Gabe Kopley avatar Feb 08 '15 06:02 Gabe Kopley
Aceptado

Encontré un informe de problema de la versión 8 que trata precisamente sobre 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 de una función en la pila o en un objeto "contexto" que se encuentra en el montón. Asignará variables locales en la pila siempre que la función no contenga ninguna función interna que haga referencia a ellas. Es una optimización . Si alguna función interna hace referencia 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 evales especial: si una función interna lo llama, todas las variables locales se colocan en el objeto de contexto.

El motivo del objeto de contexto es que, en general, podría devolver una función interna desde 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 tiene que 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. Respecto al problema encontrado en la depuración, un miembro del proyecto dice :

La única solución que se me ocurre es que siempre que devtools esté activado, eliminaremos todo el código y lo volveremos a compilar con una asignación de contexto forzada. Sin embargo, eso reduciría drásticamente el rendimiento con las herramientas de desarrollo habilitadas.

Aquí hay un ejemplo de "si alguna función interna se refiere a la variable, colóquela en un objeto de contexto". Si ejecuta esto, podrá acceder xa la debuggerdeclaración aunque xsolo se use en la foofunción, ¡que nunca se llama !

function baz() {
  var x = "x value";
  var z = "z value";

  function foo () {
    console.log(x);
  }

  function bar() {
    debugger;
  };

  bar();
}
baz();
Louis avatar Feb 10 '2015 12:02 Louis

Como dijo @Louis, fue causado por optimizaciones de v8. Puede recorrer la pila de llamadas hasta el marco donde esta variable es visible:

llamada1 llamada2

O reemplazar debuggercon

eval('debugger');

evaleliminará el fragmento actual

OwnageIsMagic avatar Nov 11 '2016 16:11 OwnageIsMagic

También noté esto en nodejs. Creo (y admito que esto es solo una suposición) que cuando el código se compila, si xno aparece dentro bar, no está xdisponible dentro del alcance de bar. Probablemente esto lo haga un poco más eficiente; el problema es que alguien olvidó (o no le importó) que incluso si no hay ningún xin bar, es posible que decida ejecutar el depurador y, por lo tanto, aún necesite acceder xdesde el interior bar.

David Knipe avatar Feb 09 '2015 22:02 David Knipe

¡Guau, realmente interesante!

Como otros han mencionado, esto parece estar relacionado con scope, pero más específicamente, relacionado con debugger scope. Cuando el script inyectado se evalúa en las herramientas de desarrollo, parece determinar un ScopeChain, lo que resulta en cierta extravagancia (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! )

function foo() {
  var x = "bat";
  var y = "man";

  function bar() {
    console.log(x); // logs "bat"

    debugger; // Attempting to access "y" throws the following
              // Uncaught ReferenceError: y is not defined
              // However, x is available in the scopeChain. Weird!
  }
  bar();
}
foo();

Para los ambiciosos y/o curiosos, busquen (je) la fuente para ver qué está pasando:

https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger

Jack avatar Feb 10 '2015 03:02 Jack