You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
(function(){console.log(typeoffoo);// function pointerconsole.log(typeofbar);// undefinedvarfoo='hello',bar=function(){return'world';};functionfoo(){return'hello';}}());
这篇文章中,我们将深入了解JavaScript中的『执行上下文』。在文章结束的时候,你应该对”解释器要做什么“、”为什么有些函数和变量可以在被声明之前就使用,它们的值是如何被确定的“有一个清楚的认识。
什么是执行上下文?
JavaScript中代码运行时的执行环境是非常重要的,通常是以下情况的一种:
网上有很多关于作用域的文章,本文的目的是让你更容易理解它,我们用下面的代码来作说明。这段代码同时包含了Global和Function的上下文。
这段代码没什么特别的,紫色框圈起来的是全局上下文,绿色框、蓝色框、橙色框圈起来的代码包含了3个函数上下文。全局上下文是唯一的,程序中的任何地方均可以访问。函数上下文可以有多个,每个函数上下文会创建一个私有上下文,这个私有上下文可以访问这个函数外部上下文中声明的变量,然而外部上下文却不能被外部作用域直接访问。
执行上下文栈
浏览器中JavaScript解释器是单线程的,这就是说同一时间代码只会做一件事,有其他行为或事件处于队列中的情况被称作执行上下文栈。下图示意了单线程栈:
![image](https://cloud.githubusercontent.com/assets/4928035/16143233/19c3b814-349e-11e6-9bdc-6da6eb8584e8.png)
正如大家所知,浏览器初次加载脚本时,默认进入全局上下文。如果你的全局代码中调用了一个函数,那么程序将会进入这个被调用函数的上下文,创建一个新的执行上下文,并把当前上下文放到执行队列的顶端。
如果你在当前函数中又调用了另外一个函数,会发生和上面同样的过程。浏览器总是会把当前执行上下文放到栈的顶部,一旦函数执行完成,这个执行上下文就会从栈中移除,返回到栈中的下一个上下文。
下面的例子展示了一个递归函数的执行栈:
这段代码只是简单调用自身3次,每次执行了
foo
函数都会创建一个新的执行上下文。一旦一个上下文执行完毕,它就被移除了并自动开始下一个执行上下文,直到返回到_全局上下文_。执行栈的关键5点:
详解执行上下文
现在我们知道了,每次函数调用都会产生一个新的执行上下文。然而,JavaScript解释器内部对于这个过程却是有2个阶段:
可以用一个有3个属性值的对象来表示执行上下文的概念:
激活对象/变量对象[AO/VO]
函数被调用时被执行前
executionContextObj
对象就被创建了,这就是阶段1(创建阶段)。解释器通过扫描函数的参数及传入的参数、局部函数声明和局部变量声明来创建executionContextObj
,得到的结果就是executionContextObj
中的variableObject
。下面是对这个过程的伪概述:
来看个例子:
调用foo(22)时,创建阶段就像下面这样:
如你所见,创建阶段处理“给属性定义名称但是不赋值,但是形式参数除外(既定义名称又赋值)”,创建阶段完毕后,就开始执行函数了,第2阶段函数执行完毕后如下:
一个词——置顶
网上有很多关于JavaScript中置顶的文章,都阐述了置顶即是把函数声明和变量声明都放到函数作用域的顶部。然而并没有文章详细解释为什么会这样,其实用一个新的视角去解释翻译器如何创建
activation object
是非常简单的。看看下面的例子:现在我们可以回答这个问题了:
总结
现在希望你对JavaScript是如何解释代码有一个清晰的认识,理解了执行上下文和栈就可以让你清楚地明白为什么你的代码和你开始预期的执行结果不一样。
你是否认为理解解释器内部工作原理需要花费很大的代价呢?
理解了执行上下文是否帮助你写出更好的JavaScript代码呢?
原文
The text was updated successfully, but these errors were encountered: