《JavaScript高级程序设计》总结(三)——执行上下文栈和变量对象

364 阅读4分钟

执行上下文栈

大家都知道js是单线程的,所以这就意味着它一次只能做一个件事情,而一个js的文件中必然会执行多个操作,比如全局代码,函数代码,每执行一次就会生成多个执行上下文,js中采用栈的方式来处理它,大家都知道栈的执行是先进的后出,因此全局的执行上下文永远是最底层的,在程序执行完毕的时候出栈

我们举个栗子来模拟一下执行上下文栈的流程

    <script>
          
          function EC(){
              let b = 1 
              console.log(b)
          }
          EC()

    </script>

1.首先我们把执行上下文栈假设成一个数组,这样比较好理解,当代码还未执行时,执行上文栈是空的

 ECStack = [];

2.当上述代码刚进入script标签后,就进入了一个全局的环境,此时的全局环境的执行上下文就会入栈,我们用globalContext来表示全局的执行上下文

 ECStack = [
    globalContext
 ]

3.我们接着来看我们的代码,此时js遇到了如下代码

       function EC(){
              let b = 1 
              console.log(b)
        }

此时编译器遇到了函数,它会生成一个函数的执行上下文并压入栈中,我们用ECContext来表示该函数的执行上下文

  //此时的ECStack中多了一个ECContext,而globalContext被压到了栈底
 ECStack = [
    ECContext,
    globalContext
 ]

4.再接着看我们的代码,此时该执行函数EC了,此时EC的执行上下文内部会做一些处理,这个我们后面会讲到,现在我们只关心执行上文栈是怎么操作的

//当EC函数执行完后,在栈顶的ECContext会弹出,栈底的globalContext会到栈顶的位置,此时的ECStack
            ECStack = [
                globalContext
            ]
            

当该js被销毁的时候,globalContext会弹出栈,ECStack被回收,至此一个完整的js就走完了

大家可能会有些疑问,频频提到的执行上下文究竟做了些什么?

执行上下文(Execution Context)

什么是执行上下文?来看定义

一段可以执行的代码在被执行的时候,会创建一个执行上下文,执行上下文,可以理解为当前代码的执行环境

那么什么是可执行代码呢?

  • 全局代码(全局环境):你的代码首次执行的默认环境,例如加载外部的js文件或者本地标签内的代码。全局代码不包括任何function体内的代码。 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境。
  • 函数代码(函数环境):当函数被调用执行时,会进入当前函数中执行环境
  • eval(不建议使用,可忽略)

执行上下文(Execution Context)

对于每个执行上下文都有三个重要的属性,它们分别是

  • 变量对象(Variable object,VO)
  • 作用域链(Scope chain)
  • this的指向

执行上下文的变量对象(Variable object,VO))

我们先来分析下变量对象Variable object,VO到底是什么

执行上下文在创建的时候会先创建变量对象voVO保存了函数中的所有形参,实参,局部变量,this指针等函数执行时的函数内部的数据情况(此时还没有执行代码),在全局执行上下文中叫做变量对象VO,在函数执行上下文中叫做活动对象AO,其实两个是一样的,具体如下

1.函数的形参和实参(如果是函数上下文)

  • 由名称和对应值组成的一个变量对象的属性被创建
  • 没有实参传入,形参就用undefined表示

2.函数的声明,function max()

  • 由函数的名称和内容来创建一个变量对象

3.变量的声明,如 var a

  • 由名称和undefined来创建一个变量对象

我们还用分析执行上下文栈时的代码改造下来举栗子


        function EC(a) {
            var b = 1
            console.log(b)
            console.log(a)
            var c = function (){}
            function c(){}
        }
        EC(2)

此时的AO为,函数还没有被执行

AO = {
    arguments:{
        0:2,
        length: 1
    },
    a:2,
    b:undefined,
    c:undefined,
    d:reference to function d(){}
}

当执行EC函数的代码时,AO会依次赋值,当执行完毕后,此时的AO

AO = {
    arguments:{
        0:2,
        length: 1
    },
    a:2
    b:1,
    c:reference to FunctionExpression "c"
    d:reference to function c(){}
} 

至此变量对象的创建过程就完结了