作用域链是函数定义时固化、按“当前→外层→全局”单向查找的路线图;变量访问取决于词法嵌套而非调用位置;var可提升访问,let/const受TDZ限制;闭包即保留未销毁的外层作用域链。

作用域链直接决定了 JavaScript 中变量从哪里开始找、按什么顺序找、最终找到谁。它不是运行时动态生成的路径,而是在函数定义时就固化下来的查找路线图。
变量查找严格按“当前→外层→全局”单向推进
引擎访问一个变量时,不会跳着找,而是逐层向上检查:
- 先查当前执行上下文的词法环境(比如函数内部用 let 声明的变量)
- 没找到,就顺着函数创建时保存的 [[Environment]] 引用,进入外层函数的作用域
- 继续没找到,再往上走,直到全局作用域
- 全程找不到,就抛 ReferenceError
词法作用域让查找结果在写代码时就已确定
函数能访问哪些外部变量,不取决于它被谁调用,而取决于它在源码中写在哪一层嵌套里:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- const x = 'global'; function outer() { const x = 'outer'; return function inner() { console.log(x); }; } const fn = outer(); fn(); // 输出 'outer',不是 'global'
- 即使把 inner 拿出去单独调用,它依然沿用定义时的外层作用域,不会因为调用位置改变而切换查找目标
var、let/const 在作用域链中的表现不同
虽然都走同一条链,但它们的“存在时间点”不一样,影响实际能否访问:
立即学习“Java免费学习笔记(深入)”;
- var 声明会被提升并初始化为 undefined,所以在函数内任何位置都能访问到该变量名(值可能是 undefined)
- let/const 虽然也登记进作用域链,但在真正执行到声明语句前处于 暂时性死区(TDZ),此时访问直接报错,不给 undefined 的缓冲机会
闭包是作用域链的自然结果,不是特殊技巧
当一个函数返回后,它的执行上下文本该销毁,但如果内部函数还在引用它的变量,JS 就会保留这条作用域链上的外层环境记录:
- function makeCounter() { let count = 0; return () => ++count; } const c1 = makeCounter(); c1(); // 1 c1(); // 2
- 每次调用 makeCounter 都新建一个独立的 count 和对应的作用域链片段,所以多个闭包互不影响

















