javascript 为什么Chrome调试器认为关闭的局部变量未定义?

vuv7lop3  于 2023-01-24  发布在  Java
关注(0)|答案(7)|浏览(117)

使用此代码:

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

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

我得到了这个意想不到的结果:

当我更改代码时:

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

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

我得到了预期的结果:

此外,如果在内部函数中有任何对eval的调用,我可以按照自己的意愿访问变量(传递给eval的内容无关紧要)。
同时,Firefox开发工具在这两种情况下都给予了预期的行为。
Chrome的调试器比Firefox的调试器更不方便,这是怎么回事?我观察这种行为已经有一段时间了,包括41.0.2272.43测试版(64位)。
是Chrome的javascript引擎在可能的时候“扁平化”了这些函数吗?
有趣的是,如果我添加第二个变量,该变量在内部函数中被引用,x变量仍然是未定义的。
我知道在使用交互式调试器时,作用域和变量定义经常会有一些问题,但在我看来,基于语言规范,应该有一个“最佳”的解决方案来解决这些问题。因此,我很好奇这是否是由于Chrome比Firefox优化得更深入。此外,这些优化是否可以在开发过程中轻易禁用(也许在开发工具打开时应该禁用它们?)。
此外,我可以使用断点和debugger语句重现这一点。

bxjv4tth

bxjv4tth1#

我已经找到了一个v8 issue report,这正是你所要求的。
现在,总结一下问题报告中所说的...... v8可以将函数的局部变量存储在堆栈上一个“上下文”对象中,该对象位于堆上。它将在堆栈上分配局部变量,只要该函数不包含任何引用它们的内部函数。这是一个优化。如果 * 任何 * 内部函数引用局部变量,该变量将被放入一个上下文对象中(即放在堆上而不是堆栈上)。eval的情况是特殊的:如果它被内部函数调用,则 all 局部变量被放入上下文对象。
使用context对象的原因是,通常情况下,你可以从外部函数返回内部函数,然后外部函数运行时存在的堆栈将不再可用,因此内部函数访问的任何内容都必须在外部函数之后继续存在,并存在于堆中,而不是堆栈中。
调试器无法检查堆栈上的那些变量。关于调试中遇到的问题,一个项目成员说:
我能想到的唯一解决方案是,无论何时devtools打开,我们都将取消选择所有代码,并使用强制上下文分配重新编译,但这将大大降低devtools打开时的性能。
下面是一个“如果任何内部函数引用了变量,将其放入上下文对象”的例子。如果你运行这个命令,你将能够在debugger语句中访问x,即使x只在foo函数中使用,* 它从来没有被调用过 *!

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

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

  function bar() {
    debugger;
  };

  bar();
}
baz();
7dl7o3gd

7dl7o3gd2#

就像@Louis说的,这是v8优化造成的。你可以遍历调用栈到这个变量可见的帧:
第一节第一节第一节第一节第一次
或者将debugger替换为

eval('debugger');

eval将取消选择当前块

hc8w905p

hc8w905p3#

我在nodejs中也注意到了这一点,我相信(我承认这只是一个猜测),当代码编译时,如果x没有出现在bar中,那么xbar的作用域中就不可用,这可能会使它稍微更高效一些;问题是有些人忘记了(或者不关心)即使bar中没有x,您也可能决定运行调试器,因此仍然需要从bar内部访问x

ca1c2owp

ca1c2owp4#

哇,真有意思!
正如其他人提到的,这似乎与scope有关,但更具体地说,与debugger scope有关。当在开发人员工具中评估注入的脚本时,它似乎确定了ScopeChain,这导致了一些奇怪的情况(因为它绑定到检查器/调试器范围)。
(EDIT- 实际上,你在最初的问题中提到了这一点,* 哎呀,我的错! *)

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();

对于那些有野心和/或好奇的人来说,看看源代码是怎么回事:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspectorhttps://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger

fv2wmkja

fv2wmkja5#

我怀疑这与变量和函数提升有关。JavaScript将所有变量和函数声明放在定义它们的函数的顶部。http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/
我敢打赌,Chrome调用断点时变量在作用域中不可用,因为函数中没有其他内容。这似乎行得通:

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

  function bar() {
    console.log(x); 
    debugger;
  };
  bar();
}

就像这样:

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

  function bar() {
    debugger;
    console.log(x);     
  };
  bar();
}

希望这个,和/或上面的链接有帮助。这些是我最喜欢的一类SO问题,顺便说一句:)

zzlelutf

zzlelutf6#

我似乎可以访问_thisthis在chrome inspector中未定义,_this似乎引用了适当的上下文(可能是堆栈跟踪inspector中的> local > this?)。

qyzbxkaa

qyzbxkaa7#

我知道这是有点老,但我的问题是缩小使用巴别塔- i.e. -预置缩小
当我的js代码被编译和缩小时,我的局部变量是未定义的;当没有缩小,我能够看到在控制台变量的值。

相关问题