函数执行上下文与作用域链是一体两面,作用域链由词法环境的outer引用链式构成,在函数定义时静态确立,执行时激活查找;闭包通过保留外层词法环境阻止其回收,而非改变作用域链结构。

函数执行上下文和作用域链不是静态绑定的两个独立概念,而是一体两面:作用域链是执行上下文内部的关键结构,它的“动态性”其实来自执行上下文的创建时机与词法环境的嵌套关系——不是运行时决定的,而是定义时就固化,但只有在执行时才被激活和使用。
函数执行上下文是作用域链的载体
每次函数被调用,JS引擎都会创建一个新的函数执行上下文(FEC),这个上下文里包含三个核心部分:变量环境、词法环境、this值。其中,词法环境(Lexical Environment)直接承载作用域链:
- 每个词法环境都包含一个「环境记录」(存储 let/const 等声明)和一个「外部环境引用」(指向父级词法环境);
- 这个外部引用逐层向上链接,最终指向全局词法环境,整条链就是作用域链;
- 也就是说,作用域链不是单独生成的,它就是词法环境通过 outer 引用自然形成的链式结构。
作用域链在函数定义时确立,而非调用时
JS采用词法作用域(静态作用域),这意味着作用域链的结构在函数被定义那一刻就确定了,跟它在哪里被调用无关:
- 比如一个在对象方法里定义的内层函数,即使被赋值给全局变量再调用,它的作用域链依然包含原定义位置的外层函数环境;
- 箭头函数的 this 和作用域链都继承自定义时的外层上下文,进一步印证这种“定义即锁定”的特性;
- eval 和 with 会临时修改作用域链,但属于例外且不推荐使用,不影响主流逻辑。
执行阶段才真正启用作用域链查找
虽然链已存在,但它只在变量或函数实际被访问时才启动查找流程:
立即学习“Java免费学习笔记(深入)”;
- 执行到
console.log(x)时,引擎先查当前 FEC 的词法环境记录; - 没找到就顺着 outer 引用跳到上一级词法环境,重复查找;
- 直到全局环境仍找不到,就抛出 ReferenceError;
- 这个查找过程发生在执行阶段,但路径本身在创建阶段就已组装完毕。
闭包是这种关系最典型的体现
当函数返回后,其执行上下文本该销毁,但若内部函数引用了外层变量,JS引擎会保留外层的词法环境(及其作用域链节点),让闭包持续访问:
- 闭包不是“延长”了作用域链,而是阻止了链上某一层词法环境被回收;
- 作用域链结构未变,只是某些环节的内存未释放;
- 这也解释了为什么闭包容易引发内存泄漏——本该销毁的执行上下文因被引用而滞留。


















