您是范围优化内部机制的第一手观察者。作用域优化是检查当前作用域中使用了哪些变量,并优化对未使用变量的访问。之所以会这样,是因为在 javascript 的 JIT 编译生成的机器码中,变量命名的整个概念都丢失了。但是,为了保持 javascript 合规性,JIT 编译器将一组使用的局部变量关联到每个 javascript 函数。观察以下代码。
(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))
})();
如上所示,Javascript 是一种基于范围的基于堆栈的语言。如果 Javascript 不是作用域语言,那么函数中使用的变量将取决于函数执行位置处的变量值。例如,如果没有范围,scopedOne
将使用|Ref1|myVariable
的值 (NaN) 而不是在|Ref2| (101) 并且会记录NaN
到控制台。回到重点,在机器代码中,当调试器进入时,它只能找出内存中的实际位置是已使用变量的位置,因为只有那些内存位置已持久保存到机器代码,因为只有那些变量已被使用. 其余变量的内存位置对它来说仍然是个谜。正如您所观察到的,这具有使范围内未使用的变量对该函数“不可见”的次要副作用。但是,有一个解决方案。
为了规避这个问题,只需将debugger;
语句包装在 eval 中,以强制浏览器对范围内的所有变量进行昂贵的变量查找。基本上,浏览器必须回到原始源代码,检查范围内变量的原始名称,并找出 JIT 生成的机器代码将变量的值存储在哪里。打开开发者工具并运行下面的代码片段。然后进入“Call Stack”面板中的上一级,观察变量值的可见性如何y
从可见的内部eval
变为不可见的外部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();