Búsqueda de sitios web

javascript - ¿Por qué el depurador de Chrome piensa que una variable local cerrada es indefinida?


Con este código:

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

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

Obtengo 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 dentro de la función interna, puedo acceder a mi variable como quiero (no importa lo que pase a eval).

Mientras tanto, las herramientas de desarrollo de Firefox muestran el comportamiento esperado en ambas circunstancias.

¿Qué pasa con Chrome que el depurador se comporta menos convenientemente que en Firefox? He observado este comportamiento durante algún tiempo, hasta e incluyendo la versión 41.0.2272.43 beta (64 bits).

¿Es que el motor de JavaScript de Chrome "aplasta" las funciones cuando puede?

Curiosamente, si añado una segunda variable que sí se referencia en la función interna, la variable x sigue sin estar definida.

Entiendo que a menudo hay peculiaridades con el alcance y la definición de variables al usar un depurador interactivo, pero me parece que según la especificación del lenguaje debería haber una solución "óptima" para estas peculiaridades. Así que tengo mucha curiosidad por saber si esto se debe a que Chrome optimiza más que Firefox. Y también si estas optimizaciones se pueden desactivar fácilmente durante el desarrollo (¿quizás deberían desactivarse cuando las herramientas de desarrollo están abiertas?).

Además, puedo reproducir esto con puntos de interrupción así como con la instrucción debugger.

Respuesta #1

He encontrado un informe de problemas de V8 que trata precisamente sobre lo que estás preguntando.

Ahora, para resumir lo que se dice en ese informe de problemas... v8 puede almacenar las variables que son locales a una función en la pila o en un objeto "contexto" que vive en el heap. 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 pondrá en un objeto de contexto (es decir, en el heap en lugar de en la pila). El caso de eval es especial: si se llama en absoluto desde 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ías devolver una función interna desde la externa y entonces la pila que existía mientras la función externa se ejecutaba ya no estará disponible. Por lo tanto, todo lo que la función interna acceda 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 esas variables que están en la pila. Con respecto al problema encontrado durante la depuración, un Miembro del Proyecto dice:

La única solución en la que pude pensar es que cada vez que las devtools estén activadas, desoptimizáramos todo el código y lo recompiláramos con asignación de contexto forzada. Sin embargo, eso deterioraría drásticamente el rendimiento con las devtools activadas.

Aquí hay un ejemplo de "si alguna función interna hace referencia a la variable, colócala en un objeto de contexto". Si ejecutas esto, podrás acceder a x en la instrucción debugger aunque x solo se use en la función foo, ¡la cual nunca se llama!

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

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

  function bar() {
    debugger;
  };

  bar();
}
baz();

Respuesta #2

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

O reemplaza debugger con

eval('debugger');

eval desoptimizará el fragmento actual

Artículos relacionados