1、执行上下文的概念
为了表示不同的运行环境,JavaScript中有一个执行上下文(Execution context,EC)的概念。代码运行是在一定的环境之中运行的,这个运行环境我们就成为执行环境,也就是执行上下文。
javascript运行的代码环境有三种:
全局代码:代码默认运行的环境,最先会进入到全局环境中
函数代码:在函数的局部环境中运行的代码
Eval代码:在Eval()函数中运行的代码
全局上下文是最外围的一个执行环境,web浏览器中被认为是window对象。在初始化代码时会先进入全局上下文中,每当一个函数被调用时就会为该函数创建一个执行上下文,每个函数都有自己的执行上下文。为了便于理解看下图
上图中,一共用4个执行上下文。紫色的代表全局的上下文;绿色代表person函数内的上下文;蓝色以及橙色代表person函数内的另外两个函数的上下文。
注意:不管什么情况下,只存在一个全局的上下文,该上下文能被任何其它的上下文所访问到。也就是说,我们可以在person的上下文中访问到全局上下文中的sayHello变量,当然在函数firstName或者lastName中同样可以访问到该变量。至于函数上下文的个数是没有任何限制的,每到调用执行一个函数时,引擎就会自动新建出一个函数上下文,换句话说,就是新建一个局部作用域,可以在该局部作用域中声明私有变量等,在外部的上下文中是无法直接访问到该局部作用域内的元素的。
2、执行环境的生命周期
执行上下文的生命周期:创建 -> 执行 -> 回收
2.1. 创建阶段
- 创建作用域链(变量对象+父级执行环境的变量对象)
- 创建变量对象(包括局部变量、函数以及函数参数)
- 确定 this 的指向
由此,一个执行环境可以由包含作用域链、变量对象和 this 指针的对象组成:
executionContextObj = {
scopeChain: {},
variableObject: {},
this: {}
}
2.2. 代码执行阶段
- 指定变量的值和函数的引用
- 解释并执行代码
2.3. 回收阶段
执行上下文出栈被垃圾回收机制进行回收。
3、JavaScript执行上下文过程(执行栈)
在JavaScript
中,用栈的方式来管理执行上下文,我们可以称它为执行栈
,也可以叫做函数调用栈
(Call Stack)。栈是遵循“先进后出,后进先出”的顺序来进行存储。
我们可以把栈理解成为一个只有一个开口的容器,先放进去的东西处于容器的最底部,而且如果要拿出来的话,它也是最后一个被拿出来(先进后出)。
在栈中,处于最底部的就是我们最开始创建的全局执行上下文对象
,它永远是处于栈底。而处于栈顶就是当前正在执行
的上下文。也就是当前执行的函数执行上下文会被放在栈顶。
栈的执行流程分为入栈、执行、出栈三个部分。
- 入栈:代码执行的时候,上下文被创建,并被压入栈中
- 执行:被压入的上下文中处于栈顶的就是当前的执行上下文,当这个函数被调用完成之后,上下文对象被销毁
- 出栈:当上下文被销毁后会从栈顶被弹出,称为出栈,紧接着下一个上下文开始执行。
下面我们通过一个栗子来加深理解:
function text(){
console.log('text')
function text1(){
console.log('text1')
}
text1()
}
text()
复制代码
执行流程:
- 创建全局上下文环境,
全局执行上下文入栈
,处于栈底 - 遇到
text()
,创建函数text
的上下文对象,text
的执行上下文入栈 - 在函数
text
内部执行可执行代码,遇到text1()
,创建text1
的上下文对象,text1
的执行上下文入栈 -
text1()
代码执行完之后没有新的函数了,那么text1()
执行完毕,弹出 -
text1()
弹出后接着执行text()
的代码,同样没有遇到新的函数,那么text()
执行完毕,弹出 - 此时,页面栈中就只剩下
全局上下文
在栈底,如果这个时候页面关闭了,那么全局上下文也弹出栈
4、总结:
- 单线程
- 同步执行
- 唯一的一个全局上下文
- 函数的执行上下文的个数没有限制
- 每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
- 在
JavaScript
执行上下文的过程中,对于所有的上下文对象,只有栈顶的上下文是正在被执行的,其他上下文是处于等待的状态,这也是因为JavaScript
是单线程所致 -
全局上下文
在整个调用栈永远都是处于栈底,而且只有唯一的一个,当页面处于活动状态的时候,全局上下文是一直存在的,直到关闭页面它才销毁。