【问题标题】:Why does Chrome debugger get undefined when accessing variables in Closure? [duplicate]为什么 Chrome 调试器在访问 Closure 中的变量时未定义? [复制]
【发布时间】:2016-09-07 04:17:46
【问题描述】:

代码:

function test4() {
    var x = 10;
    var y = 100;
    // inner referred x only
    function inner () {
        console.log(x);
        debugger;
    }
    // inner2 referred y to make sure y is in the scope of inner
    function inner2 () {
        console.log(y);
    }
    return inner;
}
var foo = test4();
foo();

yinner 的范围内,甚至只有inner2 从未使用过的引用它。我检查了范围内的结果,xy 在那里:

但是当我在监视面板和控制台中检查变量时,我无法获取所有变量:

奇怪的是y 在范围内但在使用调试器时没有被定义。 那么,这是否意味着调试器无法访问当前上下文中未使用的变量,即使它在闭包中还是只是一个错误? (我的 chrome 版本是 51.0.2704.103 m)

它类似于Why does Chrome debugger think closed local variable is undefined?,但不一样。因为inner2 在我的代码中确保y 在闭包中。实际上我的问题与该问题下的Louis's answer相反。

【问题讨论】:

  • 实际上不是。不同之处在于“inner2()”使用了“y”。如果去掉 'inner2()' 部分,'y' 不会关闭,这是Gabe's question 的情况。但有趣的是,路易斯在这个问题下的回答表明我的情况应该是不可能的。
  • 我很确定这是一回事。 inner 闭包没有引用y,所以它被优化了,调试器无法访问它。 inner2 可以,但你不在inner2
  • 所以你的意思是innerinner2inner2的上下文中有不同的上下文和'y',但在inner的上下文中没有?如果是真的,y 不能在范围的封闭部分。并且变量y可以被GC收集(因为只有inner2引用它,而inner2永远不会被使用或引用)所以this的第三个例子不会导致内存泄漏。我的意思是innerinner2 可能引用相同的上下文,它们共享一个包含xy 的对象。
  • 嗯,我怀疑它的优化方式可能还有其他因素在起作用。也许您可以扩展您的问题以包含其中一些信息并澄清它以获得潜在的答案?
  • y 没有被引用,所以它被优化了。没有任何东西需要它存在(并且可供调试器访问)。

标签: javascript google-chrome google-developer-tools


【解决方案1】:

您是范围优化内部机制的第一手观察者。范围优化是检查当前范围中使用了哪些变量,并优化对未使用变量的访问。之所以会这样,是因为在 javascript 的 JIT 编译生成的机器码中,变量命名的整个概念都丢失了。但是,为了保持 javascript 合规性,JIT 编译器将一组使用的局部变量关联到每个 javascript 函数。观察以下代码。

(function(){
  "use strict";
  var myVariable = NaN; // |Ref1|
  var scopedOne = (function(){
    var myVariable = 101; // |Ref2|
    return x => x * myVariable;
  })();
  var scopedTwo = (function(){
    var myVariable = -7; // |Ref3|
    return x => x / myVariable;
  })();
  console.log("scopedOne(2):  ", scopedOne(2));
  console.log("scopedTwo(56): ", scopedTwo(56))
})();

如上所示,Javascript 是一种基于范围的基于堆栈的语言。如果 Javascript 不是作用域语言,那么函数中使用的变量将取决于函数执行位置处的变量值。例如,如果没有范围,scopedOne 将在 |Ref1| (NaN) 处使用 myVariable 的值,而不是在 |Ref2| (101) 处使用将NaN 记录到控制台。回到重点,在机器代码中,当调试器进入时,它只能找出内存中的实际位置是使用变量的位置,因为只有那些内存位置已经持久化到机器代码中,因为只有那些变量已经被使用.其余变量的内存位置对它来说仍然是个谜。正如您所观察到的,这具有使范围内未使用的变量对该函数“不可见”的次要副作用。但是,有一个解决方案。

为了规避这个问题,只需将debugger; 语句包装在一个eval 中,强制浏览器对范围内的所有变量进行昂贵的变量查找。基本上,浏览器必须回到原始源代码,检查范围内变量的原始名称,并找出 JIT 生成的机器代码将变量的值存储在哪里。打开开发者工具并运行下面的sn-p。然后转到“调用堆栈”面板中的上一级,观察变量y 的值的可见性如何从eval 内部可见变为eval 外部不可见。

function test4() {
    var x = 10;
    var y = 100;
    // inner referred x only
    function inner () {
        console.log(x);
        eval("debugger;");
    }
    // inner2 referred y to make sure y is in the scope of inner
    function inner2 () {
        console.log(y);
    }
    return inner;
}
var foo = test4();
foo();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    • 1970-01-01
    相关资源
    最近更新 更多