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

时间:2015-02-07 23:04:44

标签: javascript google-chrome google-chrome-devtools

使用此代码:

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

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

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

enter image description here

当我更改代码时:

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

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

我得到了预期的结果:

enter image description here

此外,如果内部函数中有eval的任何调用,我可以按照自己的意愿访问我的变量(对于我传递给eval的内容并不重要。)< / p>

与此同时,Firefox开发工具在两种情况下都会给出预期的行为。

与Chrome有什么关系,调试器的行为不如Firefox好?我已观察到这种行为已有一段时间了,包括版本41.0.2272.43 beta(64位)。

这是Chrome的javascript引擎&#34;变平&#34;可以的功能吗?

有趣的是,如果我在内部函数中添加 引用的第二个变量,x变量仍未定义。

据我所知,在使用交互式调试器时,通常会有范围和变量定义的怪癖,但在我看来,根据语言规范,应该有一个最好的&#34;这些怪癖的解决方案。所以我很好奇这是因为Chrome比Firefox更优化。以及在开发过程中是否可以轻松禁用这些优化(当开发工具打开时,它们应该被禁用吗?)。

另外,我可以使用断点和debugger语句重现这一点。

6 个答案:

答案 0 :(得分:134)

我找到了一个v8 issue report,这正是你所要求的。

现在,总结该问题报告中的内容... v8可以在一个&#34;上下文&#34;中存储函数的本地变量。生活在堆上的对象。它将在堆栈上分配局部变量,只要该函数不包含任何引用它们的内部函数。 这是一项优化。如果任何内部函数引用局部变量,则此变量将被放入上下文对象中(即在堆上而不是在堆栈上)。 eval的情况很特殊:如果内部函数完全调用它,所有局部变量都放在上下文对象中。

上下文对象的原因是,通常你可以从外部函数返回一个内部函数,然后在外部函数运行时存在的堆栈再也不可用了。所以内部函数访问的任何东西都必须在外部函数中存活并且存在于堆上而不是堆栈上。

调试器无法检查堆栈中的那些变量。关于调试中遇到的问题,有一个项目成员says

  

我能想到的唯一解决方案是,无论何时启用devtools,我们都会取消所有代码并使用强制上下文分配重新编译。尽管如此,这将大大降低性能,但启用了devtools。

这里是&#34;如果任何内部函数引用变量,则将其放在上下文对象中#34;的示例。如果您运行此操作,即使x仅在debugger函数中使用了x,您也可以在foo语句中访问function baz() { var x = "x value"; var z = "z value"; function foo () { console.log(x); } function bar() { debugger; }; bar(); } baz(); 从未打过电话!

{{1}}

答案 1 :(得分:24)

就像@Louis所说的那样是由v8优化造成的。 您可以遍历调用堆栈到此变量可见的框架:

http://c-faq.com/stdio/scanfprobs.html call1

或用

替换debugger
eval('debugger');

eval将取消当前的块

答案 2 :(得分:6)

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

答案 3 :(得分:2)

哇,真有意思!

正如其他人所提到的,这似乎与scope有关,但更具体地说,与debugger scope有关。当在开发人员工具中评估注入脚本时,它似乎确定了ScopeChain,这导致一些奇怪(因为它绑定到检查器/调试器范围)。您发布的内容的变体是:

(编辑 - 实际上,你在原来的问题中提到这个, yikes,我的坏!

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

对于雄心勃勃和/或好奇,范围(heh)的消息来源,看看发生了什么:

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

答案 4 :(得分:0)

我怀疑这与变量和功能提升有关。 JavaScript将所有变量和函数声明带到它们定义的函数的顶部。更多信息在这里:http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/

我敢打赌,Chrome正在使用范围不可用的变量调用断点,因为该函数中没有其他内容。这似乎有效:

&#13;
&#13;
function baz() {
  var x = "foo";

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

就像这样:

&#13;
&#13;
function baz() {
  var x = "foo";

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

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

答案 5 :(得分:0)

我似乎可以访问 const。对于我来说,Chrome 检查器中 _this 未定义,this 似乎引用了适当的上下文(并且可能是堆栈跟踪检查器中用于 _this 的内容?)。