使用此代码:
function baz() { var x = "foo"; function bar() { debugger; }; bar(); } baz();
我得到了这个意想不到的结果:
当我更改代码时:
function baz() { var x = "foo"; function bar() { x; debugger; }; bar(); }
我得到了预期的结果:
此外,如果eval在内部函数中有任何调用,我可以按照我的意愿访问我的变量(不管我传递给什么eval)。
eval
同时,Firefox 开发工具在这两种情况下都给出了预期的行为。
Chrome 是怎么回事,调试器的行为不如 Firefox 方便?我已经观察到这种行为一段时间了,直到并包括版本 41.0.2272.43 beta(64 位)。
Chrome的javascript引擎是否可以“扁平化”功能?
有趣的是,如果我添加在内部函数 中x引用的第二个变量,该变量仍然是未定义的。
x
我知道在使用交互式调试器时经常会出现范围和变量定义的怪癖,但在我看来,基于语言规范应该有一个“最佳”的解决方案来解决这些怪癖。所以我很好奇这是否是由于 Chrome 比 Firefox 优化得更远。还有这些优化是否可以在开发过程中轻松禁用(也许在开发工具打开时应该禁用它们?)。
此外,我可以使用断点和debugger语句重现这一点。
debugger
我找到了一份 v8问题报告,这正是您要问的内容。
现在,总结一下该问题报告中所说的内容...... v8 可以将本地函数存储在堆栈上 或 位于堆上的“上下文”对象中。只要函数不包含任何引用它们的内部函数,它将在堆栈上分配局部变量。 这是一种优化 。如果 任何 内部函数引用了一个局部变量,这个变量将被放入一个上下文对象中(即在堆上而不是在栈上)。的情况eval很特殊:如果它完全被内部函数调用,则 所有 局部变量都放在上下文对象中。
上下文对象的原因是,通常您可以从外部函数返回一个内部函数,然后外部函数运行时存在的堆栈将不再可用。因此,内部函数访问的任何内容都必须在外部函数中存在并存在于堆上而不是堆栈上。
调试器无法检查堆栈上的那些变量。关于调试中遇到的问题,一位项目成员说:
我能想到的唯一解决方案是,只要打开 devtools,我们就会取消所有代码并使用强制上下文分配重新编译。但是,如果启用了 devtools,这将大大降低性能。
这是“如果任何内部函数引用变量,则将其放入上下文对象”的示例。如果您运行它,您将能够访问x该debugger语句,即使x它仅用于 从未调用过foo的函数中! __
foo
function baz() { var x = "x value"; var z = "z value"; function foo () { console.log(x); } function bar() { debugger; }; bar(); } baz();