为什么 Chrome 调试器认为关闭的局部变量未定义?
Why does Chrome debugger think closed local variable is undefined?
使用此代码:
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 beta(64 位)。
是不是 Chrome 的 javascript 引擎 "flattens" 可以正常运行?
有趣的是,如果我在内部函数中添加 是 引用的第二个变量,x
变量仍未定义。
我知道在使用交互式调试器时,范围和变量定义经常会出现问题,但在我看来,根据语言规范,应该有 "best" 解决这些问题的方法。所以我很好奇这是否是由于 Chrome 优化比 Firefox 更进一步。还有这些优化是否可以在开发过程中轻松禁用(也许它们应该在开发工具打开时禁用?)。
此外,我可以使用断点和 debugger
语句重现此内容。
我怀疑这与变量和函数提升有关。 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();
}
希望这个和/或上面的 link 有所帮助。这些是我最喜欢的 SO 问题,顺便说一句 :)
我在 nodejs 中也注意到了这一点。我相信(并且我承认这只是一个猜测)当代码被编译时,如果 x
没有出现在 bar
中,它不会使 x
在范围内可用bar
。这可能会使它稍微更有效率;问题是有人忘记(或不在乎)即使 bar
中没有 x
,您可能决定 运行 调试器,因此仍然需要访问 x
从里面 bar
.
哇,真有趣!
正如其他人提到的,这似乎与 scope
有关,但更具体地说,与 debugger scope
有关。当在开发人员工具中评估注入的脚本时,它似乎确定了一个 ScopeChain
,这导致了一些古怪(因为它绑定到 inspector/debugger 范围)。您发布的内容的变体是:
(编辑 - 实际上,你在原来的问题中提到了这一点,哎呀,我的错!)
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();
对于雄心勃勃的 and/or 好奇的人,请查看(呵呵)源代码以查看发生了什么:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger
我找到了一个 v8 issue report,它正是您要问的。
现在,总结一下该问题报告中所说的...v8 可以将函数的局部变量存储在堆栈 或 中 "context" 存在于堆上的对象。只要函数不包含引用它们的任何内部函数,它就会在堆栈上分配局部变量。 这是一个优化。如果 any 内部函数引用一个局部变量,这个变量将被放入一个上下文对象中(即在堆上而不是在堆栈上)。 eval
的情况很特殊:如果它完全被内部函数调用,所有 个局部变量都放在上下文对象中。
上下文对象的原因是,通常您可以 return 来自外部函数的内部函数,然后在外部函数 运行 不再可用时存在的堆栈.因此,内部函数访问的任何内容都必须在外部函数中存活下来,并存在于堆上而不是堆栈上。
调试器无法检查堆栈上的那些变量。关于调试中遇到的问题,一位Project Member says:
The only solution I could think of is that whenever devtools is on, we would deopt all code and recompile with forced context allocation. That would dramatically regress performance with devtools enabled though.
这是 "if any inner function refers to the variable, put it in a context object" 的示例。如果你 运行 这个你将能够在 debugger
语句中访问 x
即使 x
仅在 foo
函数中使用,从未被调用!
function baz() {
var x = "x value";
var z = "z value";
function foo () {
console.log(x);
}
function bar() {
debugger;
};
bar();
}
baz();
正如@Louis 所说,这是由 v8 优化引起的。
您可以遍历调用堆栈到此变量可见的帧:
或将debugger
替换为
eval('debugger');
eval
将解除当前块
我似乎可以访问 _this
。 this
在 chrome 检查器中对我来说是未定义的,_this
似乎引用了适当的上下文(并且可能是在堆栈跟踪检查器中用作 > local > this
的内容?)。
使用此代码:
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 beta(64 位)。
是不是 Chrome 的 javascript 引擎 "flattens" 可以正常运行?
有趣的是,如果我在内部函数中添加 是 引用的第二个变量,x
变量仍未定义。
我知道在使用交互式调试器时,范围和变量定义经常会出现问题,但在我看来,根据语言规范,应该有 "best" 解决这些问题的方法。所以我很好奇这是否是由于 Chrome 优化比 Firefox 更进一步。还有这些优化是否可以在开发过程中轻松禁用(也许它们应该在开发工具打开时禁用?)。
此外,我可以使用断点和 debugger
语句重现此内容。
我怀疑这与变量和函数提升有关。 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();
}
希望这个和/或上面的 link 有所帮助。这些是我最喜欢的 SO 问题,顺便说一句 :)
我在 nodejs 中也注意到了这一点。我相信(并且我承认这只是一个猜测)当代码被编译时,如果 x
没有出现在 bar
中,它不会使 x
在范围内可用bar
。这可能会使它稍微更有效率;问题是有人忘记(或不在乎)即使 bar
中没有 x
,您可能决定 运行 调试器,因此仍然需要访问 x
从里面 bar
.
哇,真有趣!
正如其他人提到的,这似乎与 scope
有关,但更具体地说,与 debugger scope
有关。当在开发人员工具中评估注入的脚本时,它似乎确定了一个 ScopeChain
,这导致了一些古怪(因为它绑定到 inspector/debugger 范围)。您发布的内容的变体是:
(编辑 - 实际上,你在原来的问题中提到了这一点,哎呀,我的错!)
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();
对于雄心勃勃的 and/or 好奇的人,请查看(呵呵)源代码以查看发生了什么:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger
我找到了一个 v8 issue report,它正是您要问的。
现在,总结一下该问题报告中所说的...v8 可以将函数的局部变量存储在堆栈 或 中 "context" 存在于堆上的对象。只要函数不包含引用它们的任何内部函数,它就会在堆栈上分配局部变量。 这是一个优化。如果 any 内部函数引用一个局部变量,这个变量将被放入一个上下文对象中(即在堆上而不是在堆栈上)。 eval
的情况很特殊:如果它完全被内部函数调用,所有 个局部变量都放在上下文对象中。
上下文对象的原因是,通常您可以 return 来自外部函数的内部函数,然后在外部函数 运行 不再可用时存在的堆栈.因此,内部函数访问的任何内容都必须在外部函数中存活下来,并存在于堆上而不是堆栈上。
调试器无法检查堆栈上的那些变量。关于调试中遇到的问题,一位Project Member says:
The only solution I could think of is that whenever devtools is on, we would deopt all code and recompile with forced context allocation. That would dramatically regress performance with devtools enabled though.
这是 "if any inner function refers to the variable, put it in a context object" 的示例。如果你 运行 这个你将能够在 debugger
语句中访问 x
即使 x
仅在 foo
函数中使用,从未被调用!
function baz() {
var x = "x value";
var z = "z value";
function foo () {
console.log(x);
}
function bar() {
debugger;
};
bar();
}
baz();
正如@Louis 所说,这是由 v8 优化引起的。 您可以遍历调用堆栈到此变量可见的帧:
或将debugger
替换为
eval('debugger');
eval
将解除当前块
我似乎可以访问 _this
。 this
在 chrome 检查器中对我来说是未定义的,_this
似乎引用了适当的上下文(并且可能是在堆栈跟踪检查器中用作 > local > this
的内容?)。