在生活中,同样的文字放在不同的场合,会有不同甚至完全相反的意思,比如让人头疼的汉语四六级(搞笑)。这个说话的场合,其实就是我们说话的语境,也是说话谈论的前提。同样的,对程序语言进行“解读”的时候,也必须在特定的语境中,这个语境就是javascript中的执行上下文。
执行上下文是什么?
以上是javascript上下文的通俗理解,标准的说法是,执行上下文(Execution Context)是javascript代码执行的环境,js代码一定是在执行上下文中执行的。
在js中,执行上下文的类型有以下三重:
全局执行上下文
全局执行上下文是JS代码最开始运行时的默认环境。如果是浏览器的话,就会创建一个全局的window对象,这也是当前页面所有js代码的基础环境,一个页面只会有一个全局执行上下文。
函数执行上下文
程序中有许多的函数,函数在被调用的时候就会创建对应的执行上下文,而且是每次调用都会创建新的执行环境。
eval函数执行上下文
js的eval函数执行其内部的代码会创建属于自己的执行上下文,已不被推荐使用,所以在这里仅作了解,不再详细讨论它了。
综合起来,从执行上下文的角度来看js运作过程就是:当页面加载时,JavaScript运行时首先会进入全局环境,生成全局上下文(一个)。程序中各种函数的依次调用,就会进入函数执行环境,对应就会生成该函数的执行上下文。
JS中管理多个执行上下文的方式
以上可知,js代码运行过程中除了最开始的全局执行上下文,还有有各种函数的调用产生的函数执行上下文,那么js是怎么管理这些上下文的呢?
首先,我们要知道的之前提到的一个知识,js是单线程的。意思是js不能同时干两件事情,必须是一个一个的执行。那么这么多的执行上下文是必然要按一定的顺序执行,这个顺序是怎么决定的呢? 现实中解决这类问题的方法是叫号,摇号,但js中这么整就完全乱套了。因为js的函数调用是有逻辑关系的,那如何保证这种逻辑关系能够正确执行呢?
答案是:栈。
在JavaScript中,通过栈的存取方式来管理执行上下文,我们可称其为执行栈,或函数调用栈(Call Stack)。栈遵循”先进后出,后进先出”的规则来进行存放取出管理,也称LIFO (“Last In First Out”) 规则。通俗的来说,栈就是只有一个开口的容器,往里放东西和往外拿东西都是通过这个出口,最新放进去的东西在最底部,所以要最后一个拿出来,最后放进去的东西(最靠近容容器开口)反而在最上面,第一个拿出来。
执行栈(函数调用栈)的工作过程
有了以上的知识准备,我们来模拟下页面从打开到关闭的整个过程中,js如何使用执行栈来管理众多执行上下文的。
整个过程的就是三个操作的不断重复:入栈、执行、出栈。入栈:程序执行进入一个执行环境时,它的执行上下文就会被创建,并被压入执行栈中;执行:被压入栈而处于”栈顶”的是当前正在执行函数的执行上下文,当函数调用完成后,它就会从栈顶被推出;出栈:程序执行完成时,它的执行上下文就会被销毁,并从栈顶被推出(出栈),控制权交由下一个执行上下文。js就是通过这种方式从而完成一段又一段代码和功能的。
第一步,进入全局上下文环境,全局执行上下文入栈,因为是第一个,所以在栈底部;
第二步,执行代码,调用函数funA,创建函数funA的执行上下文,并入栈;
第三步,执行函数funA内部代码,调用函数funB,创建函数funB的执行上下文,并入栈;
第四步,执行函数funB内部代码,该执行上下文结束,funB执行上下文出栈;
第五步,执行执行函数funA内部代码调用funB函数之后代码,funA执行上下文出栈;
此时页面未关闭,执行栈中仅留下全局执行上下文在栈底。
这就是整个执行上下文的管理过程。
需要特殊说明的是:因为JS执行中页面加载就会进入全局环境,所以处于”栈底的永远是全局环境的执行上下文”,这个全局会在页面关闭的时候被取出。在出栈的过程中,正常情况下当函数调用完成后,它就会从栈顶被推出,但是如果有闭包就会阻止该操作,也就是执行上下文不会被释放。
相比之前,是不是对闭包有了更深的理解呢!理解执行上下文,对于理解作用域以及作用域链都有很大的帮助。前端虽广,保持学习,终将拿下!
发表评论