438 字
2 分钟
07 · 执行上下文与调用栈
“单线程”的真正含义
JS 在浏览器里是单线程的——同一时刻只做一件事。代码执行靠**调用栈(Call Stack)**管理:函数调用就压栈,执行完弹出。
执行上下文
每段代码运行前,JS 引擎创建执行上下文,包含:变量环境、作用域链、this。分为:
- 全局上下文:最外层,程序启动时创建,栈底。
- 函数上下文:每次函数调用创建一个,压入栈顶。
- eval 上下文:少用,忽略。
调用栈长这样
function a() { b(); }function b() { c(); }function c() { console.log("end"); }a();执行时栈:a → a,b → a,b,c → 打印 → 依次弹出 c,b,a。调试时 DevTools 的 Call Stack 面板就是它。
栈溢出
递归没有终止条件,栈被压满:
function recurse() { recurse(); }recurse(); // RangeError: Maximum call stack size exceeded注意:这是同步栈溢出。异步递归(在
setTimeout里调自己)不会爆栈,因为每次都出栈后再入栈。
和事件循环的关系
调用栈空了,事件循环才会把任务队列里的回调推入栈执行。这是下一篇异步的核心铺垫——同步代码永远先跑完,异步回调排队等。
小结
- JS 单线程,靠调用栈管理函数执行。
- 每次调用创建执行上下文并压栈,结束弹出。
- 递归无终止 → 栈溢出。
- 栈空后事件循环才调度异步回调(下篇展开)。
练习
- 在 DevTools 里给
c()打断点,观察 Call Stack 从a→b→c的变化。 - 写一个会栈溢出的递归,再改成”尾递归+setTimeout”避免溢出,对比两者。
- 解释:为什么
setTimeout(fn, 0)里的fn一定在同步代码之后才执行?