你该知道的JS知识---执行上下文栈
执行流程
当我们调用一个函数时,一个新的执行上下文就会被创建。
一个执行上下文的生命周期可分为创建阶段和代码执行阶段
创建阶段:在这个阶段中,执行上下文会分别进行以下操作
- 创建
变量对象 - 建立
作用域链 - 确定
this指向 代码执行阶段:创建完成之后,就会开始执行代码,并依次完成以下步骤 - 变量赋值
- 函数引用
- 执行其他代码
可执行代码
每次当控制器转到可执行代码的时候,就会进入一个执行上下文。
执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。
JavaScript中的运行环境大概包括三种情况:
- 全局环境:JavaScript代码运行起来会首先进入该环境
- 函数环境:当函数被调用执行时,会进入当前函数中执行代码
- eval(不建议使用,可忽略)
因此在一个JavaScript程序中,必定会产生多个执行上下文,而JavaScrpit引擎会以栈的方式来处理,这个栈,我们称作函数调用栈(Call Stack)。栈底永远都是全局上下文,而栈顶就是当前执行的上下文。
当代码在执行过程中,遇到以上三种情况,都会创建一个执行上下文,放入栈中,而处于栈顶的上下文执行完毕之后,就会自动出栈。
栈堆分析
JavaScript引擎通过创建执行上下文栈(Execution Context Stack,ECS) 用于管理执行上下文。 为了模拟执行上下文栈的行为,我们用数组来类别执行上下文栈。
ECStack=[]
试想当JavaScript开始要解释执行代码的时候,最先遇到的就是全局代码,所以初始化的时候就首先把全局执行上下文压入执行上下文栈中,我们用globalContext表示,并且只有当整个应用程序结束的时候,ECStack才会被清空,所以程序结束之前,ECStack最底部永远有个globalContext。
ECStack=[globalContext];
假如JavaScript遇到下面的这段代码:
function fun3(){
console.log('fun3');
}
function fun2(){
fun3();
}
function fun1(){
fun2();
}
fun1();
...
当执行一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会把函数的执行上下文从栈中弹出。
知道了这样的工作原理,让我们来看看如何处理的上面代码:
//fun1()
ECStack.push(<fun1>functionContext);
//fun1函数中调用了fun2函数,还要创建fun2的执行上下文
ECStack.push(<fun2>functionContext);
//fun2函数里面还调用了fun3,继续创建fun3的执行上下文
ECStack.push(<fun3>functionContext);
//fun3执行完毕
ECStack.pop();
//fun2执行完毕
ECStack.pop();
//fun1执行完毕
ECStack.pop();
//JavaScrpit接着执行下面的代码,但是ECStack底层永远有个globalContext
详细了解了这个过程之后,我们就可以对执行上下文进行一点总结了。
- JavaScript引擎是单线程的
- 同步执行,只有栈顶的上下文处于执行中1,其他上下文需要等待
- 全局上下文只有唯一的一个,它在浏览器关闭的时候出栈
- 函数执行上下文的个数是没有限制的
- 每次某个函数被调用的时候,就会创建对应的执行上下文,即使是调用自身函数也是如此