执行上下文栈
执行上下文
执行上下文分为三种:
- 全局执行上下文 (
globalContext) - 函数执行上下文 (
functionContext) - eval 执行上下文
对于每个执行上下文,都有三个重要属性:
执行过程
一个执行上下文的生命周期可以分为两个阶段。
- 创建阶段:构建作用域链,创建变量对象,确定
this指向。 - 执行阶段:完成变量赋值和函数引用,解释执行代码。
创建阶段
在执行代码之前,会先创建执行上下文并对其包含的属性进行初始化。
- 作用域链
- 复制函数内部属性
[[scope]]来构建作用域链 - 将变量(活动)对象压入其作用域链的顶端
- 复制函数内部属性
- 变量对象
- 函数形参(仅限函数执行上下文):如果没有实参,属性值设为
undefined。 - 函数声明:如果变量对象已经存在相同名称的属性,则完全 替换 这个属性。
- 变量声明:如果变量名称跟已经声明的形参或函数相同,则变量声明 不会干扰 已存在的这类属性。
- 函数形参(仅限函数执行上下文):如果没有实参,属性值设为
- this
- 词法绑定
- new 绑定
- 显式绑定
- 隐式绑定
- 默认绑定
执行阶段
解释执行代码,在这个过程中不断修改变量对象中的属性值。
执行上下文栈
JS 引擎创建了很多执行上下文,为方便管理,创建了执行上下文栈 ( Execution Context Stack ) 。
初始化时,最先碰到的就是全局代码,因而会向执行上下文栈压入一个全局执行上下文。程序执行完毕,执行栈清空,就会将最底部的全局执行上下文从栈中弹出。
遇到函数执行时,会向执行上下文栈压入一个函数执行上下文。函数执行完毕,就会将函数的执行上下文从栈中弹出。

TIP
具体执行分析可参考冴羽大大的 JavaScript 深入之执行上下文
概念区别
WARNING
个人理解,可能不够准确,有待推敲。
作用域和变量对象
作用域 ≈ 变量对象
两者基本是一个概念
作用域和执行上下文
- 执行上下文 > 作用域
- 执行上下文 = 变量对象 + 作用域链 + this
- 作用域链 = 变量对象 + 父环境作用域链
- 作用域 ≈ 变量对象
作用域链和执行上下文区别
- 作用域链用于查找变量
- 执行上下文栈用于管理执行上下文
执行上下文栈和调用栈
同一个东西
思考题
根据闭包和词法作用域的知识,我们知道以下两个案例的结果是一样的。那么这两段代码究竟有何不同呢?答案就是执行上下文栈的变化不一样。
var scope = 'global scope'
function checkscope() {
var scope = 'local scope'
function f() {
return scope
}
return f()
}
checkscope()
// 执行上下文栈变化
ECStack.push(<checkscope> functionContext)
ECStack.push(<f> functionContext)
ECStack.pop()
ECStack.pop()
var scope = 'global scope'
function checkscope ( ) {
var scope = 'local scope'
function f() {
return scope
}
return f
}
checkscope()()
// 执行上下文栈变化
ECStack.push(<checkscope> functionContext)
ECStack.pop()
ECStack.push(<f> functionContext)
ECStack.pop()