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:
But when I checked variables in watch panel and console, I can't get all of them:
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.
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.
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.
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.
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With