使用此代码:
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测试版(64位)。
是Chrome的javascript引擎在可能的时候“扁平化”了这些函数吗?
有趣的是,如果我添加第二个变量,该变量在内部函数中被引用,x
变量仍然是未定义的。
我知道在使用交互式调试器时,作用域和变量定义经常会有一些问题,但在我看来,基于语言规范,应该有一个“最佳”的解决方案来解决这些问题。因此,我很好奇这是否是由于Chrome比Firefox优化得更深入。此外,这些优化是否可以在开发过程中轻易禁用(也许在开发工具打开时应该禁用它们?)。
此外,我可以使用断点和debugger
语句重现这一点。
7条答案
按热度按时间bxjv4tth1#
我已经找到了一个v8 issue report,这正是你所要求的。
现在,总结一下问题报告中所说的...... v8可以将函数的局部变量存储在堆栈上或一个“上下文”对象中,该对象位于堆上。它将在堆栈上分配局部变量,只要该函数不包含任何引用它们的内部函数。这是一个优化。如果 * 任何 * 内部函数引用局部变量,该变量将被放入一个上下文对象中(即放在堆上而不是堆栈上)。
eval
的情况是特殊的:如果它被内部函数调用,则 all 局部变量被放入上下文对象。使用context对象的原因是,通常情况下,你可以从外部函数返回内部函数,然后外部函数运行时存在的堆栈将不再可用,因此内部函数访问的任何内容都必须在外部函数之后继续存在,并存在于堆中,而不是堆栈中。
调试器无法检查堆栈上的那些变量。关于调试中遇到的问题,一个项目成员说:
我能想到的唯一解决方案是,无论何时devtools打开,我们都将取消选择所有代码,并使用强制上下文分配重新编译,但这将大大降低devtools打开时的性能。
下面是一个“如果任何内部函数引用了变量,将其放入上下文对象”的例子。如果你运行这个命令,你将能够在
debugger
语句中访问x
,即使x
只在foo
函数中使用,* 它从来没有被调用过 *!7dl7o3gd2#
就像@Louis说的,这是v8优化造成的。你可以遍历调用栈到这个变量可见的帧:
第一节第一节第一节第一节第一次
或者将
debugger
替换为eval
将取消选择当前块hc8w905p3#
我在nodejs中也注意到了这一点,我相信(我承认这只是一个猜测),当代码编译时,如果
x
没有出现在bar
中,那么x
在bar
的作用域中就不可用,这可能会使它稍微更高效一些;问题是有些人忘记了(或者不关心)即使bar
中没有x
,您也可能决定运行调试器,因此仍然需要从bar
内部访问x
。ca1c2owp4#
哇,真有意思!
正如其他人提到的,这似乎与
scope
有关,但更具体地说,与debugger scope
有关。当在开发人员工具中评估注入的脚本时,它似乎确定了ScopeChain
,这导致了一些奇怪的情况(因为它绑定到检查器/调试器范围)。(EDIT- 实际上,你在最初的问题中提到了这一点,* 哎呀,我的错! *)
对于那些有野心和/或好奇的人来说,看看源代码是怎么回事:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspectorhttps://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger
fv2wmkja5#
我怀疑这与变量和函数提升有关。JavaScript将所有变量和函数声明放在定义它们的函数的顶部。http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/
我敢打赌,Chrome调用断点时变量在作用域中不可用,因为函数中没有其他内容。这似乎行得通:
就像这样:
希望这个,和/或上面的链接有帮助。这些是我最喜欢的一类SO问题,顺便说一句:)
zzlelutf6#
我似乎可以访问
_this
。this
在chrome inspector中未定义,_this
似乎引用了适当的上下文(可能是堆栈跟踪inspector中的> local > this
?)。qyzbxkaa7#
我知道这是有点老,但我的问题是缩小使用巴别塔- i.e. -预置缩小
当我的js代码被编译和缩小时,我的局部变量是未定义的;当没有缩小,我能够看到在控制台变量的值。