javascript执行上下文栈是后入先出的内存结构,用于实时管理函数调用关系;每个上下文经历创建(初始化vo/le、this、作用域链)和执行(赋值、运算)两阶段;var存于变量环境并提升,let/const存于词法环境且受tdz限制;栈溢出源于嵌套过深导致超出引擎深度限制。

JavaScript 执行上下文栈(也叫调用栈)不是代码执行的“顺序记录”,而是引擎实时管理函数调用关系的内存结构。它不记录历史,只反映当前正在运行或等待返回的上下文层级。
执行上下文栈是后入先出的容器
每当函数被调用,引擎就创建一个函数执行上下文,并把它压入栈顶;函数执行完毕,该上下文立即弹出。栈底永远是全局执行上下文,栈顶永远是当前正在执行的代码环境。
- 全局脚本开始执行时,全局执行上下文入栈
- 调用函数 A → A 的上下文入栈(此时栈:A → 全局)
- A 中调用函数 B → B 的上下文入栈(此时栈:B → A → 全局)
- B 执行结束 → B 出栈,控制权交还给 A
- A 执行结束 → A 出栈,最终只剩全局上下文
每种上下文对应明确的生命周期阶段
每个执行上下文都经历两个不可跳过的阶段:创建阶段和执行阶段。这两个阶段决定了变量能否访问、this 指向谁、以及作用域链如何构建。
- 创建阶段:扫描所有 var/let/const 声明和函数声明,初始化变量对象(VO)和词法环境(LE),确定 this 和作用域链
- 执行阶段:按代码顺序真正赋值、运算、调用函数——此时才发生实际的数据写入和副作用
- 注意:var 声明在创建阶段就绑定到变量环境并设为 undefined;let/const 虽也被扫描,但直到声明语句执行前都处于暂时性死区(TDZ)
变量环境与词法环境是分开管理的
这是理解 var/let/const 行为差异的关键。V8 引擎为不同声明方式分配了不同的存储空间:
- 变量环境:只存放 var 声明和函数声明,支持变量提升
- 词法环境:存放 let/const 声明,支持块级作用域,且不参与提升
- 两者独立存在,互不影响。比如 let a = 1 和 var a = 2 在同一作用域中不会冲突,因为它们存放在不同环境里
栈溢出本质是上下文嵌套过深
递归调用没有终止条件,会导致不断创建新函数上下文并压栈,最终超出引擎允许的最大深度(通常约 10000–15000 层),抛出 RangeError。
- 这不是内存耗尽,而是调用栈结构本身的容量限制
- 尾递归优化(TCO)在严格模式下部分支持,但主流浏览器仍未默认启用
- 避免方式:改用循环、增加终止判断、或使用异步分片(如 setTimeout 拆解)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











