执行上下文栈

执行上下文

执行上下文分为三种:

  • 全局执行上下文 ( globalContext )
  • 函数执行上下文 ( functionContext )
  • eval 执行上下文

对于每个执行上下文,都有三个重要属性:

执行过程

一个执行上下文的生命周期可以分为两个阶段。

  • 创建阶段:构建作用域链,创建变量对象,确定 this 指向。
  • 执行阶段:完成变量赋值和函数引用,解释执行代码。

创建阶段

在执行代码之前,会先创建执行上下文并对其包含的属性进行初始化。

  • 作用域链
    • 复制函数内部属性 [[scope]] 来构建作用域链
    • 将变量(活动)对象压入其作用域链的顶端
  • 变量对象
    • 函数形参(仅限函数执行上下文):如果没有实参,属性值设为 undefined
    • 函数声明:如果变量对象已经存在相同名称的属性,则完全 替换 这个属性。
    • 变量声明:如果变量名称跟已经声明的形参或函数相同,则变量声明 不会干扰 已存在的这类属性。
  • this
    • 词法绑定
    • new 绑定
    • 显式绑定
    • 隐式绑定
    • 默认绑定

执行阶段

解释执行代码,在这个过程中不断修改变量对象中的属性值。

执行上下文栈

JS 引擎创建了很多执行上下文,为方便管理,创建了执行上下文栈 ( Execution Context Stack ) 。

初始化时,最先碰到的就是全局代码,因而会向执行上下文栈压入一个全局执行上下文。程序执行完毕,执行栈清空,就会将最底部的全局执行上下文从栈中弹出。

遇到函数执行时,会向执行上下文栈压入一个函数执行上下文。函数执行完毕,就会将函数的执行上下文从栈中弹出。

excution_context_stack

TIP

具体执行分析可参考冴羽大大的 JavaScript 深入之执行上下文open in new window

概念区别

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()

参考

Last Updated:
Contributors: Vsnoy