--- title: JavaScript进阶(三)— 执行上下文栈 createTime: 2020/02/11 11:53:33 author: pengzhanbo permalink: /article/6snk1b6c/ --- 关于 执行上下文,请点击查看[这篇文章](/article/d12xkizf/)。 ## 执行上下文栈 `JavaScript` 引擎 创建了 执行上下文栈 来存储并管理 代码执行时创建的所有 执行上下文。 执行上下文栈(Execution context stack,ECS) 是一个种拥有 LIFO(后进先出) 的栈。 我们使用一个数组 模拟 执行上下文栈: ```js const ECSStack = [] ``` 当 `JavaScript` 执行时,首先遇到的是 全局代码,初始化时,会首先向 执行上下文栈中压入 全局执行上下文 (global execution context)。它只有在整个程序结束时,才会被清空,所以在程序结束前, `ECSStack` 底部 都会有一个 `globalExecutionContext` ```js const ECSStack = [ globalExecutionContext ] ``` 当 `JavaScript` 开始执行以下代码时: ``` js function foo() { console.log('foo') } function bar() { foo() } function run() { bar() } run() ``` 在这段代码中,`JavaScript` 进行以下处理: ``` ts // 伪代码: // run(), 创建 函数执行上下文,并压入 执行上下文栈 ECSStack.push(functionExecutionContext) // run() 中发现需要执行 bar(), 继续创建 函数执行上下文,并压入执行上下文栈 ECSStack.push(functionExecutionContext) // bar() 中发现需要执行 foo(), 继续创建 函数执行上下文,并压入执行上下文栈 ECSStack.push(functionExecutionContext) /** 此时, ECSStack 的结构,如下: * [ * globalExecutionContext, * functionExecutionContext, * functionExecutionContext, * functionExecutionContext * ] */ // 当 foo() 执行完毕,从执行上下文栈中移除 functionExecutionContext ECSStack.pop() // 当 bar() 执行完毕,从执行上下文栈中移除 functionExecutionContext ECSStack.pop() // 当 run() 执行完毕,从执行上下文栈中移除 functionExecutionContext ECSStack.pop() // 在程序未结束时, ECSStack 底部永远有一个 globalExecutionContext ``` 再看下一个例子: ```js var scope = "global scope"; function checkScope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkScope(); ``` ```js var scope = "global scope"; function checkScope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkScope()(); ``` 这两段代码,虽然在执行结果是一样的,都是输出 `local scope`, 但是在 执行上下文栈 中的变化不同。 第一段代码, 在 执行上下文栈,模拟处理如下: ```ts // 执行checkScope() ECSStack.push(functionExecutionContext) // 在checkScope执行阶段中执行 f() ECSStack.push(functionExecutionContext) ECSStack.pop() ECSStack.pop() ``` 第二段代码, 在执行上下文栈,模拟处理如下: ```ts // 执行checkScope() ECSStack.push(functionExecutionContext) ECSStack.pop() // 在checkScope 完成后,再执行 f() ECSStack.push(functionExecutionContext) ECSStack.pop() ``` 可以看出, 函数执行的时机不同,虽然最终结果一致,但是在 执行上下文栈 中的过程是不同的。 ## 总结 1. 执行上下文栈,是用于存储和管理所有执行上下文。 2. 在程序没有结束前,执行栈中永远有一个全局执行上下文。 3. 函数执行时,会创建一个新的函数执行上下文,并按顺序压入到执行栈中。 4. 函数执行完成后,对应的函数执行上下文会从执行栈中移除。