执行上下文
javascript在执行语句之前,经过了一系列准备,为代码执行创建了一个执行上下文
全局执行上下文 & 函数执行上下文
首先我们可以通过代码的位置,把所创建出来的上下文分成 全局执行上下文 和 函数执行上下文
🌰 1 :
//全局执行上下文
console.log(a1) // a1 = window.a1 undefined
a2() //a2 = window.a2()
console.log(this) //window
var a1 = 3
function a2 (){
console.log('a2')
}
思考:为什么a1,a2 为什么会挂载到window对象上?this 为什么就指向 window ?
我们来仔细的分析🌰1的流程
- 在执行全局代码前将 window 确定为全局执行上下文
对全局数据进行预处理:
(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执行到一个上下文中就会把它放入执行栈的栈顶,这个执行上下文叫做当前执行上下文
默认在底部有一个全局执行上下文