外观
执行上下文和执行栈是什么?
执行上下文是一次代码运行时的环境,里面有变量环境、词法环境和 this。执行栈是这些上下文叠起来的调用栈。函数调用就压栈,返回就出栈。
原理
全局代码一开始就有全局上下文。每调用一个函数,创建一个函数上下文。栈顶是正在运行的那段代码。
创建一个上下文时,引擎先处理声明:var 和函数声明会进变量环境,var 初值是 undefined,函数声明可以直接用。let 和 const 进词法环境,初始化前处于暂时性死区。然后才逐行执行。
箭头函数不创建自己的 this,它的上下文仍会入栈,但 this 沿外层词法环境找。
执行完的上下文出栈后,如果有闭包还握着它的环境,变量对象不会马上回收。见 作用域与闭包。
追问
和作用域是一回事吗?
不是。作用域是写代码时就确定的访问范围。执行上下文是运行到那一次调用时创建的。同一个函数调用两次,有两个执行上下文,作用域规则只有一套。
栈溢出怎么来的?
递归没有终止,上下文一直压栈。尾调用在严格模式且满足条件时可以被引擎复用栈帧,见 尾递归。
易错点
- 函数声明整体提升,函数表达式的变量名按
var或let的规则提升,函数体不会提前可用。 - 执行栈描述的是同步调用。
then和setTimeout的回调是以后新的一次入栈,不是留在当前栈里。 - 块级
let会单独有词法环境,不等于整个函数只有一个变量对象。