开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天、第 3 篇,点击查看活动详情
引言
我们知道Javascript代码会按顺序执行,比如如下的代码:
console.log('Hello world!') //==>
function func1(){
console.log('Hello world! I am func1.')
}
func1()
打印的结果如下
Hello world!
Hello world! I am func1.
但是看下面这串代码
function func(){
console.log('Hello world! I am func1.')
}
func()
function func(){
console.log('Hello world! I am func2.')
}
func()
尽管func的第一次执行在第二次声明之前,但是他的输出却仍然是两个'Hello world! I am func2.',有过一定理解的一定都知道只是因为js在执行前会对代码进行分析,函数声明会被提升,并且因为是顺序执行因此第一次的函数声明会被第二次的函数声明重写。那么我们的问题是JS是如何区分每一段代码并做这样的代码分析的呢?那便和我们今天要学习的执行上下文栈有关。
执行上下文栈
我们在这里把全局代码看做是一整个函数,而JS每当执行到一个函数时,就会创建一个执行上下文(Execution context stack,ECS)并且推入执行上下文栈进行统一管理,就会去做一次这样的代码分析,而当函数执行完毕时便将这个函数的执行上下文推出。
这个执行上下文栈呢我们把它看作为一个后进先出的数组
ECStack = [];
我们已下面这一段简单的代码为例,来简单说明他的执行过程:
function func3() {
console.log('I am fun3')
}
function func2() {
console.log('I am fun2')
func3();
}
function func1() {
console.log('I am fun1')
func2();
}
func1();
第一步我们便遇到了全局代码,因此此时JS就会创建一个全局执行上下文,并且入栈
var globalECS;
ECStack.push(ECSGlobal)
此时执行上下文栈中就有了一个全局执行上下文
ECStack = [ ECSGlobal ];
函数声明的过程并不会去创建新的全局执行上下文,而当代码执行到最后一行时,JS遇到了执行函数func1,此时便会创建一个func1的执行上下文,并且入栈
var func1ECS;
ECStack.push(func1ECS)
此时执行上下文栈中的情况是
ECStack = [ ECSGlobal, func1ECS ];
然后代码继续执行func1ECS,遇到console.log('I am fun1'),同样也会执行一样的操作,但是我们这里不对console.log,进行说明,只需要知道执行完毕后打印了I am fun1,之后便遇到了func2,同样会创建一个func2的执行上下文并入栈
var func2ECS;
ECStack.push(func2ECS)
此时执行上下文栈中又推入了一个func2ECS
ECStack = [ ECSGlobal, func1ECS, func2ECS ];
入栈后继续执行func2,打印完I am fun2之后便又遇到了func3,又以同样的方式创建了func2的执行上下文func3ECS并入栈
ECStack = [ ECSGlobal, func1ECS, func2ECS, func3ECS ];
然而这次打印完I am fun3之后func3便执行完毕了,函数执行完之后便会将执行上下文出栈并销毁
ECStack.pop()
此时执行上下文栈中就变回了
ECStack = [ ECSGlobal, func1ECS, func2ECS ];
之后func2和func1执行完之后便同样的做出栈的操作,并且当func1执行完毕以后,全局代码也执行完毕了,全局执行上下文同样出栈,该段代码便完成了。
总结
执行上下文只是JS运行过程中非常简单,非常基础的一部分工作,执行上下文只是一个容器,而下一篇便来探究这个执行上下文这个容器内包含的内容。最后再打个广告,关注公众号程序猿青空,免费领取191本计算机领域黑皮书电子书,更有集赞活动免费挑选精品课程(各个领域的都有),不定期分享各种优秀文章、学习资源、学习课程,能在未来(因为现在还没啥东西)享受更多福利。