作用域查找是执行上下文栈与各上下文作用域链协同的结果;栈顶上下文为当前执行环境,变量查找从其VO/LE开始,沿定义时确定、调用时拼接的作用域链逐级向上;闭包使作用域链脱离栈生命周期而存在;变量提升与TDZ由上下文创建/执行阶段决定。

作用域的查找行为,本质上是执行上下文栈与每个上下文中“作用域链”的协同结果。不是靠记忆规则,而是看栈里有什么、每个上下文里链怎么连。
执行上下文栈决定谁在“当前”
JavaScript 是单线程、同步执行的,任何时候只有一个执行上下文处于活动状态——就是调用栈最顶端的那个。这个“栈顶上下文”,就是当前代码正在运行的环境,也是变量查找的起点。
比如函数 foo 调用 bar,再调用 baz,栈从底到顶是:
全局 → foo → bar → baz。
此时只有 baz 的上下文在执行,它的变量对象(VO)或词法环境(LE)是第一查找层。
每个上下文自带一条作用域链
作用域链不是全局共享的,而是每个函数在**定义时**就静态确定并保存下来的外层环境引用,等到**调用时**,再把当前活动对象(AO)或词法环境拼到链最前面,形成完整的作用域链。
立即学习“Java免费学习笔记(深入)”;
- 函数创建时:记录它词法上所处的外层作用域(即外层函数或全局的词法环境)
- 函数调用时:新建上下文 → 把当前 AO/LE 放在链首 → 后续链接定义时保存的外层链
- 变量查找:从链首开始逐级向上,直到找到标识符,或抵达全局环境
闭包就是作用域链被“保留”下来
当一个函数返回了内部函数,而该内部函数仍被引用,它所绑定的作用域链就不会被释放——哪怕外层函数早已执行完毕、其上下文早已从栈中弹出。
例如:
function outer() {const x = 10;
return function inner() { console.log(x); };
}
const fn = outer(); // outer 执行完,上下文已出栈
fn(); // 仍能访问 x —— 因为 inner 的作用域链里还存着 outer 的词法环境
这里的关键是:作用域链的生命期不依赖执行上下文栈,而依赖函数对象是否存活。栈管“谁正在跑”,作用域链管“能访问谁”。
变量提升和暂时性死区也由上下文阶段决定
执行上下文分创建阶段和执行阶段:
- 创建阶段:var 和 function 声明被放入变量环境(VE),初始化为 undefined;let/const 声明被记入词法环境(LE),但暂不初始化(进入 TDZ)
- 执行阶段:代码逐行运行,赋值、调用发生;访问 TDZ 中的 let/const 变量会报错
这说明:作用域范围(在哪能声明/访问)由词法结构决定,但变量能否读写、初始值为何,取决于当前执行上下文所处的阶段。


















