Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Chrome debugger get undefined when accessing variables in Closure? [duplicate]

Code:

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

y is in the scope of inner even only inner2 which never been used refer to it. I checked the result in scope and x, y are there:

x and y in closure

But when I checked variables in watch panel and console, I can't get all of them:

can't get y in watch panel

It's weird that y is in the scope but get not defined when using debugger. So, is it means that debugger can not access variable that not used in current context even it's in the closure or it just a bug? (My chrome version is 51.0.2704.103 m)

It's similar to Why does Chrome debugger think closed local variable is undefined? but not the same. Because inner2 in my code make sure that y is in the closure. And actually my question is opposite to Louis's answer under that question.

like image 212
ST_Lighter Avatar asked Sep 07 '16 04:09

ST_Lighter


People also ask

Why does Chrome console say undefined?

The reason for that is that the console prints the value that is returned by the last statement. console. log(...) returns undefined (well, returns nothing, void, for that matter), so you get undefined. If the last statement returns something else (or has a value), that would be displayed.

How do I debug a variable value in Chrome?

We can then check what values are stored in any variable at that point of time. Click F12 to open Developer Tools in Chrome. Or we can right-click and select Inspect (Ctrl+Shift+I). Go to Sources tab and expand Event Listener Breakpoints section.

How can I stop Chrome from going into debug mode?

Go to the "Sources" tab. At the top right hand side, toggle the button that looks like the pause symbol surrounded by a hexagon (button on the far right) until the color of the circle turns black to turn it off. If the pause symbol isn't blue it may be that you've accidentally marked a line for debugging inspection.


1 Answers

You are a first-hand observer of the internal mechanics of scope-optimization. Scope optimization is checking to see which variables are used in the current scope and optimizing out access to unused variables. The reason for this is because in the machine code generated from JIT compilation of javascript, the whole concept of variable naming is lost. But, to maintain javascript compliance, the JIT compiler associates an array of used local variables to each javascript function. Observe the following code.

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

As seen above, Javascript is a scoped stack-based language. If Javascript was not a scoped language, then the variables used in the functions would depend on the values of the variables at the location where the function was being executed. For instance, without a scope, scopedOne would use the value of myVariable at |Ref1| (NaN) instead of at |Ref2| (101) and would log NaN to the console. Back to the main point, in the machine code, when the debugger comes in, it can only figure out where the actual locations in memory are of the used variables since only those memory locations have persisted to machine code as only those variable have been used. The memory locations of the rest of the variables remain a mystery to it. As you have observed, this has the secondary side-effect of making unused variables in the scope "invisible" to that function. However, there is a solution.

To circumvent this problem, simply wrap the debugger; statement in an eval to force the browser to do an expensive variable lookup of all the variables in the scope. Basically, the browser has to go back to the original source code, examine it for the original names of the variables in the scope, and figure out where the values of the variables are stored by the JIT-generated machine code. Open up developer tools and run the snippet below. Then go to the prior level in the "Call Stack" panel and observe how the visibility of the value of the variable y changes from visible inside eval to invisible outside 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();
like image 137
Jack G Avatar answered Sep 19 '22 08:09

Jack G