执行上下文

javascript在执行语句之前,经过了一系列准备,为代码执行创建了一个执行上下文

全局执行上下文 & 函数执行上下文

首先我们可以通过代码的位置,把所创建出来的上下文分成 全局执行上下文 和 函数执行上下文

🌰 1 :

  1. //全局执行上下文
  2. console.log(a1) // a1 = window.a1 undefined
  3. a2() //a2 = window.a2()
  4. console.log(this) //window
  5. var a1 = 3
  6. function a2 (){
  7. console.log('a2')
  8. }

思考:为什么a1,a2 为什么会挂载到window对象上?this 为什么就指向 window ?

我们来仔细的分析🌰1的流程

    1. 在执行全局代码前将 window 确定为全局执行上下文
    1. 对全局数据进行预处理:

               (1):var 定义的变量 ==> var a1 ,再添加为window的属性<br />                  (2):function 声明的全局方法,添加为window的方法<br />                  (3):this ==> 赋值(window)
      
  • 3.开始执行全局代码(打断点会发现执行代码是,直接跳过了a2的声明,原因就是其实已经处理过了)

当我们执行代码时,遇到变量或方法时 js 就会去全局执行上下文中去找变量或方法

🌰 2:

function fn(a1){
    console.log(a1)  // 2
  console.log(a2)  // undefined
  a3()  // 'a3'
  console.log(this) // window
  console.log(arguments) //伪数组 [2,3]

  var a2 = 3 
  function a3(){
      console.log('a3')
  }
}

fn(2,3)

我们通过🌰2来分析函数执行上下文

  • 在调用函数,准备执行函数之前,创建对应的函数执行上下文对象

    注意:函数上下文 并不是真实存在的对象,它是存在于栈中的一块封闭区域(可以当作对象理解)

  • 对局部数据进行预处理 :

               (1):形参变量 => 赋值(实参, a1 = 2 )==> 添加为执行上下文的属性<br />                   (2):arguments => 赋值(实参列表, arguments = [2,3] )==> 添加为执行上下文的属性<br />                   (3):var 定义的局部变量 => a2 = undefined => 添加为执行上下文的属性<br />                   (4):function 声明的函数 => 赋值(a3) => 添加为执行上下文的方法<br />                   (5):this => this = 调用函数的对象 = window
    
  • 执行函数代码

总结:
执行上下文创建分为创建阶段与执行阶段两个阶段
创建阶段主要负责三件事

  • 创建变量对象(VO VariableEnvironment ),包含变量、函数声明和函数的形参,该属性只能在全局上下文中访问
  • 创建作用域链(JS 采用词法作用域,可以把它理解成包含自身变量对象和上级变量对象的列表)
  • 确定 this

执行栈

说到这你是否会想,上下文种类不同,而且创建的数量还这么多,它们之间的关系是怎么样的,又是谁来管理这些上下文呢,这就不得不说说上下文执行栈了。

执行栈是一个存放执行上下文的地方,是一个先进后出的队列
js执行到一个上下文中就会把它放入执行栈的栈顶,这个执行上下文叫做当前执行上下文
默认在底部有一个全局执行上下文

image.png