理解JS中堆栈的调用

242 阅读3分钟

执行上下文和执行栈

执行上下文的类型

全局执行上下文: 只有一个,浏览器中就是window对象,this指向这个全局对象

函数执行上下文: 当函数被调用的时候这个函数执行上下文就会被创建,每次调用都会创建一个新的函数执行上下文。

执行栈(Execution context stack)

既然是执行栈,一定是后进先出,是用来存储代码执行期间的执行上下文的。

首次运行js代码的时候,会创建一个全局执行上下文(globalContext)push到执行栈底。然后函数执行的时候把创建的函数执行上下文push到执行栈里。当这个函数执行完成时,函数执行上下文pop出去,控制权交给下一个执行上下文。当所有代码执行完成时,全局上下文pop,运行结束。

执行上下文的创建

执行上下文的创建分为两个阶段,创建阶段执行阶段

创建阶段

1.确定this的值,也被称为this binding

全局执行上下文this指向全局对象,在浏览器中指向的时window,在node里面指向的当前的module对象。

函数执行上下文取决于函数的调用方式。

2.lexicalEnv环境(词法环境)创建

词法环境有两部分组成:

1.环境记录:存储变量和函数声明的实际位置。

2.对外部环境的引用:可以访问其外部词法环境

词法环境的两种类型

全局环境:外部环境引用为null。里面包含一个全局对象,用户自定义的全局变量,自带的方法。

函数环境:用户在函数中定义的变量被储存在环境记录中,包含了arguments对象,外部环境的引用可以是全局对象,也可以是其所在的外部函数。(函数里面套函数的形式)

3.varEnv环境(变量环境)创建

变量环境其实是专为var关键字服务的,变量环境本质上也是一个词法环境。

而使用var关键字声明的变量会存在变量提升的问题。

在创建阶段,用var 声明的变量存在varEnv中,此时访问,尽管值为undefined,但也可以访问的到。而使用const和let声明的变量是存在lexicalEnv中,保持未初始化的状态的,这时候访问就会抛错。

这就是变量提升的原因。

下面我们贴出一段代码,来看一下进入执行上下文后变量的初始化顺序

function foo(a) {
  var b = 2;
  function c() {}
  var d = function() {};
  b = 3;
}

foo(1);

此时活动对象(tips:函数所接收参数与函数内声明的变量并不冲突。)

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

执行阶段

这个阶段完成对所有变量的分配,最后执行代码。所以上述代码执行阶段的活动对象为:

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