Javascript 为什么Chrome调试器认为闭合局部变量未定义?
使用此代码:Javascript 为什么Chrome调试器认为闭合局部变量未定义?,javascript,google-chrome,google-chrome-devtools,Javascript,Google Chrome,Google Chrome Devtools,使用此代码: function baz() { var x = "foo"; function bar() { debugger; }; bar(); } baz(); 我得到了一个意想不到的结果: 当我更改代码时: function baz() { var x = "foo"; function bar() { x; debugger; }; bar(); } 我得到了预期的结果: 此外,如果在内部函数中有任何对eval的调用
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引擎在可能的情况下“扁平化”了函数
有趣的是,如果我添加在内部函数中引用的第二个变量,x
变量仍然没有定义
我知道在使用交互式调试器时,在作用域和变量定义方面经常存在一些怪癖,但在我看来,基于语言规范,应该有一个“最佳”解决方案来解决这些怪癖。所以我很好奇这是否是因为Chrome比Firefox优化得更远。以及这些优化是否可以在开发过程中轻松禁用(也许应该在开发工具打开时禁用?)
此外,我还可以使用断点以及
调试器语句来重现这一点。我怀疑这与变量和函数有关。JavaScript将所有变量和函数声明放在定义它们的函数的顶部。更多信息请点击此处:
我打赌Chrome调用断点时变量对作用域不可用,因为函数中没有其他内容。这似乎有效:
函数baz(){
var x=“foo”;
功能条(){
控制台日志(x);
调试器;
};
bar();
}
我在nodejs中也注意到了这一点。我相信(我承认这只是一个猜测),当代码被编译时,如果x
没有出现在bar
中,那么x
就不能在bar
的范围内使用。这可能会使它稍微更有效率;问题是有人忘记(或不在乎)即使bar
中没有x
,您可能决定运行调试器,因此仍然需要从bar
内部访问x
,哇,真有趣
正如其他人提到的,这似乎与范围相关,但更具体地说,与调试器范围相关。在开发人员工具中评估注入的脚本时,它似乎确定了一个范围链
,这导致了一些奇怪之处(因为它绑定到了检查器/调试器范围)。您发布内容的一个变体是:
(编辑-事实上,你在最初的问题中提到了这一点,我的错!)
对于雄心勃勃的人和/或好奇的人,请查看来源,看看发生了什么:
我发现了一款v8引擎,它正是您想要的
现在,总结一下问题报告中的内容。。。v8可以将函数的局部变量存储在堆栈或堆上的“上下文”对象中。只要函数不包含引用它们的任何内部函数,它就会在堆栈上分配局部变量这是一种优化。如果任何内部函数引用局部变量,则该变量将放在上下文对象中(即堆上而不是堆栈上)。eval
的情况很特殊:如果内部函数调用它,则所有局部变量都放在上下文对象中
使用context对象的原因是,通常可以从外部函数返回内部函数,然后外部函数运行时存在的堆栈将不再可用。因此,内部函数访问的任何内容都必须在外部函数中生存,并在堆中而不是堆栈中生存
调试器无法检查堆栈上的那些变量。关于调试中遇到的问题,一名项目成员:
我能想到的唯一解决方案是,无论何时打开devtools,我们都会对所有代码进行除臭,并使用强制上下文分配重新编译。不过,如果启用了devtools,这将大大降低性能
下面是一个“如果任何内部函数引用变量,请将其放入上下文对象”的示例。如果运行此命令,您将能够访问debugger
语句中的x
,即使x
仅在foo
函数中使用,该函数从未调用过
就像@Louis说的,它是由v8优化引起的。
您可以将调用堆栈遍历到此变量可见的帧:
或将调试器替换为
eval('debugger');
eval
将删除当前块可能它正在为您清除未使用的变量…markle976似乎在说调试器代码>行实际上不是从栏内部调用的。因此,当堆栈跟踪在调试器中暂停时,请查看堆栈跟踪:堆栈跟踪中是否提到了bar
函数?如果我是对的,那么stacktrace应该说它在第5行、第7行、第9行暂停了。我认为这与V8展平功能无关。我认为这只是一个怪癖;我不知道我是否会称之为虫子。我认为大卫下面的回答最有意义。同样,我也有同样的问题,我讨厌它。但是当我需要在控制台中有access闭包条目时,我会转到您可以看到范围的地方,找到闭包条目并打开它。然后右键单击所需的元素,然后单击“存储为全局变量”。控制台上附加了一个新的全局变量temp1
,您可以使用它访问范围条目。谢谢!)我想知道FF有什么不同之处。从我作为开发人员的角度来看,FF体验
function baz() {
var x = "x value";
var z = "z value";
function foo () {
console.log(x);
}
function bar() {
debugger;
};
bar();
}
baz();
eval('debugger');