javascript - Hvorfor tror Chrome-debbugeren at lukket lokal variabel er undefined?


Med denne koden:

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

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

Jeg får dette uventede resultatet:

Når jeg endrer koden:

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

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

Jeg får det forventede resultatet:

Også, hvis det er et kall til eval innenfor den indre funksjonen, kan jeg få tilgang til variabelen min slik jeg vil (det spiller ingen rolle hva jeg sender til eval).

I mellomtiden gir Firefox-utviklerverktøy den forventede oppførselen i begge situasjoner.

Hva er det med Chrome at debuggeren oppfører seg mindre praktisk enn Firefox? Jeg har observert denne oppførselen en stund, helt opp til og inkludert versjon 41.0.2272.43 beta (64-bit).

Er det slik at Chromes JavaScript-motor «flater ut» funksjonene når den kan?

Interessant nok, hvis jeg legger til en andre variabel som blir referert til i den indre funksjonen, er x-variabelen fortsatt udefinert.

Jeg forstår at det ofte er merkelige ting med rekkevidde og variabeldefinisjon når man bruker en interaktiv debugger, men det virker for meg som om det basert på språkspesifikasjonen burde finnes en «beste» løsning på disse merkelige tingene. Så jeg er veldig nysgjerrig på om dette skyldes at Chrome optimaliserer mer enn Firefox. Og også om disse optimaliseringene enkelt kan deaktiveres under utvikling (kanskje de burde deaktiveres når utviklerverktøyene er åpne?).

Jeg kan også reprodusere dette med breakpoints samt debugger-setningen.

Svar #1

Jeg har funnet en V8-feilrapport som handler nøyaktig om det du spør om.

Nå, for å oppsummere hva som blir sagt i den problemrapporten... v8 kan lagre variablene som er lokale for en funksjon på stakken eller i et "context" objekt som ligger på heapen. Den vil tildele lokale variabler på stakken så lenge funksjonen ikke inneholder noen indre funksjoner som refererer til dem. Det er en optimalisering. Hvis enhver indre funksjon refererer til en lokal variabel, vil denne variabelen bli plassert i et context-objekt (dvs. på heapen i stedet for på stakken). Tilfellet med eval er spesielt: hvis den blir kalt i det hele tatt av en indre funksjon, blir alle lokale variabler plassert i context-objektet.

Grunnen til kontekstavobjektet er at generelt kunne du returnere en indre funksjon fra den ytre, og da vil ikke stakken som eksisterte mens den ytre funksjonen kjørte være tilgjengelig lenger. Så alt den indre funksjonen får tilgang til må overleve den ytre funksjonen og leve på heapen i stedet for på stakken.

Feilsøkeren kan ikke inspisere de variablene som er på stakken. Når det gjelder problemet som oppstod under feilsøking, sier et Prosjektmedlem:

Den eneste løsningen jeg kunne komme på, er at hver gang devtools er på, deoptimaliserer vi all kode og kompilerer på nytt med tvungen kontekstallokering. Det vil imidlertid dramatisk redusere ytelsen når devtools er aktivert.

Her er et eksempel på «hvis en hvilken som helst indre funksjon refererer til variabelen, legg den i et kontekstobjekt». Hvis du kjører dette, vil du kunne få tilgang til x ved debugger-setningen selv om x bare brukes i foo-funksjonen, som aldri blir kalt!

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

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

  function bar() {
    debugger;
  };

  bar();
}
baz();

Svar #2

Som @Louis sa, det er forårsaket av v8-optimaliseringer. Du kan gå gjennom kallstakken til rammen der denne variabelen er synlig:

Eller erstatt debugger med

eval('debugger');

eval vil deoptimere gjeldende blokk

Relaterte artikler