1. ホーム
  2. javascript

[解決済み] Chromeデバッガーが閉じたローカル変数を未定義と判断するのはなぜですか?

2022-04-20 14:10:37

質問

このコードで

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

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

このような予期せぬ結果になってしまいます。

コードを変更すると

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

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

期待通りの結果が得られます。

また、もし eval に何を渡すかは問題ではありません)。 eval ).

一方、Firefoxの開発ツールでは、どちらの状況でも期待通りの動作が得られます。

デバッガーの挙動がFirefoxより不便なChromeはどうしたんだ?Version 41.0.2272.43 beta (64-bit)までは、以前からこの挙動を確認しています。

クロームのjavascriptエンジンが、可能な限り関数をフラットにしているのでしょうか?

面白いことに、2つ目の変数に 内部関数で参照される x 変数は未定義のままです。

対話型デバッガを使用する場合、スコープや変数定義に癖があることは理解していますが、言語仕様に基づき、これらの癖に対する "ベスト"ソリューションがあるはずだと思われます。そこで、Chrome が Firefox よりもさらに最適化していることが原因なのかどうか、非常に気になります。また、これらの最適化を開発中に簡単に無効にできるかどうかも気になります(開発ツールを開いているときに無効にすべきなのかもしれませんね)。

また、ブレークポイントを使っても再現できるのは debugger ステートメントを使用します。

どのように解決するのですか?

V8を発見 問題報告 という質問に対して、まさにその通りです。

さて、そのレポートに書かれていることを要約すると...v8は、関数にローカルな変数をスタックに格納できます。 または ヒープ上に存在するコンテキストオブジェクトの中にあります。関数がそれを参照する内部関数を含んでいない限り、スタック上にローカル変数を確保します。 最適化である . もし 任意の 内部関数がローカル変数を参照している場合、この変数はコンテキストオブジェクトに置かれます (つまり、スタック上ではなくヒープ上に置かれます)。の場合は eval は特殊で、内部関数からまったく呼び出されない場合です。 すべて のローカル変数がコンテキストオブジェクトに置かれます。

コンテキストオブジェクトが必要な理由は、一般的に外側の関数から内側の関数を返すと、外側の関数が実行されている間に存在したスタックが使えなくなるからです。そのため、内部関数がアクセスするものはすべて外部関数から生き残り、スタック上ではなくヒープ上に存在しなければなりません。

デバッガはスタック上にある変数を検査することができません。デバッグ時に発生する問題については、あるプロジェクトメンバーが とは :

私が思いついた唯一の解決策は、devtools がオンのときはいつでも、すべてのコードをデオプトし、強制的なコンテキスト割り当てで再コンパイルすることです。しかし、それではdevtoolsが有効なときにパフォーマンスが劇的に低下してしまいます。

以下は、"if any inner function refers to the variable, put it in a context object"の一例です。これを実行すると、次のようにアクセスできるようになります。 x での debugger ステートメントを使用しているにもかかわらず x が使われているのは foo 関数を使用します。 を呼び出すことはありません。 !

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

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

  function bar() {
    debugger;
  };

  bar();
}
baz();