🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## **执行上下文栈** 在 `ECMAScript` 中的代码有三种类型:`global`,`function` 和 `eval`。 每一种代码的执行都需要依赖自身的上下文,当然 `global` 的上下文可能涵盖了很多的 `function` 和 `eval` 的实例。 **函数的每一次调用,都会进入函数执行中的上下文,取得当前执行上下文中的变量,一个 `function` 可能会产生无限的上下文环境,因为每一次函数的调用(甚至递归)都会产生一个新的上下文环境。** ``` function foo(num) {} foo(10) foo(20) foo(30) // 虽然调用相同的function,但是每次都会产生不同的上下文 // 包含不同的状态,例如参数num的值 ``` 一个执行上下文可以激活另一个上下文,就好比一个函数调用了另一个函数(比如在全局的上下文中调用一个全局函数),然后一层一层调用下去。逻辑上来说,这种实现方式是栈,我们可以称之为上下文堆栈。 激活其他上下文的某个上下文被称为 *调用者(caller)* ,被激活的上下文被称为 *被调用者(callee)* 。 被调用者同时也可能是调用者(比如一个在全局上下文中被调用的函数调用某些自身的内部方法)。 当一个 *调用者(caller)* 激活了一个 *被调用者(callee)* ,那么这个 *调用者(caller)* 就会暂停它自身的执行,然后将控制权交给这个 *被调用者(callee)* ,于是这个 *被调用者(callee)* 被放入堆栈,称为进行中的上下文 `[running/active execution context]` ,当这个 *被调用者(callee)* 的上下文结束之后,又会把控制权再次交给它的 *调用者(caller)* ,然后 *调用者(caller)* 会在刚才暂停的地方继续执行。在这个 *调用者(caller)* 结束之后,会继续执行下面的代码,触发其他的上下文。一个 *被调用者(callee)* 可以用返回 `return` 或者抛出异常 `exception` 来结束自身的上下文。 如下图,所有的 `ECMAScript` 代码执行都可以看作是一个执行上下文堆栈 `[execution context (EC) stack]`。堆栈的顶部就是处于激活状态的上下文。 ![](https://box.kancloud.cn/f78f0f3c0b4651a6073da3cb42a5da1a_229x136.png) 当一段程序开始时,会先进入全局执行上下文环境 `[global execution context]`,这个也是堆栈中最底部的元素。此全局程序会开始初始化,初始化生成必要的对象和函数,在此全局上下文执行的过程中,它可能会激活一些方法(初始化过的方法),然后进入它们的上下文环境,然后将新的元素压入堆栈。在这些初始化都结束之后,这个系统会等待一些事件(例如用户鼠标事件),会触发一些方法,然后进入一个新的上下文环境。