执行上下文
什么是执行上下文
执行上下文非常重要,其中涉及了变量对象、作用域链、 this,闭包 等许多重要但并不那么容易搞清楚的概念,这些概念有助于我们真正理解 JavaScript 代码的运行机制,所以执行上下文牵扯的东西太多了,非常重要,非常重要,非常重要!!!
JavaScript代码在执行时
,会进入一个执行上下文
中。执行上下文可以理解为当前代码的运行环境
JavaScript中的运行环境主要包括以下三种情况:
全局环境:代码运行起来后会首先进入全局环境
函数环境:当函数被调用执行时,会进入当前函数中的执行代码
eval环境:不建议使用,不做介绍
JavaScript引擎会以栈的方式处理这些环境
,这个栈就是函数调用栈,函数调用栈规定了JavaScript代码的执行顺序。栈底永远是全局上下文,栈顶则是当前正在执行的上下文
举个例子:
var color = 'blue';
function changeColor() {
var anotherColor = 'red';
function swapColors() {
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
}
swapColors();
}
changeColor();
我们用ECStack
来表示处理执行上下文组的堆栈。我们很容易知道,第一步,首先是全局上下文入栈。
全局上下文入栈之后,其中的可执行代码开始执行,直到遇到了changeColor(),这一句激活函数changeColor创建它自己的执行上下文,因此第二步就是changeColor的执行上下文入栈。
changeColor的上下文入栈之后,控制器开始执行其中的可执行代码,遇到swapColors()之后又激活了一个执行上下文。因此第三步是swapColors的执行上下文入栈。
在swapColors的可执行代码中,再没有遇到其他能生成执行上下文的情况,因此这段代码顺利执行完毕,swapColors的上下文从栈中弹出。
swapColors的执行上下文弹出之后,继续执行changeColor的可执行代码,也没有再遇到其他执行上下文,顺利执行完毕之后弹出。这样,ECStack中就只剩下全局上下文了。
全局上下文在浏览器窗口关闭后出栈。
注意:函数中,遇到return能直接终止可执行代码的执行,因此会直接将当前上下文弹出栈。
详细了解了这个过程之后,我们就可以对执行上下文总结一些结论了。
单线程
同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
全局上下文只有唯一的一个,它在浏览器关闭时出栈
函数的执行上下文的个数没有限制
每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
根据
函数执行时
才会创建执行
执行上下文这一原则去理解上下文
上下文的生命周期
我们知道,当一个函数调用时,
一个新的执行上下文就会被创建
,一个执行上下文的生命周期大致分为两个阶段:创建阶段和执行阶段
创建阶段
在这个阶段,执行上下文会分别创建变量对象
、确定作用域链
,以及this指向
执行阶段
创建阶段之后,就会开始执行代码,这个时候会完成变量赋值,函数引用,以及执行其它可执行代码,如图所示
从执行上下文的生命周期可以看到它的重要性,其中涉及了变量对象、作用域链、this等许多重要但并不那么容易搞清楚的概念,这些概念有助于我们真正理解JavaScript代码的运行机制
来个例子巩固一下上面所说的:
function test() {
console.log(a);
console.log(foo());
var a = 1;
function foo() {
return 2;
}
}
test();
当运行test函数,对应的上下文创建,可以采用如下形式来表达整个过程
// 创建阶段
testEC={
VO:{}, // 变量对象
scopeChain:[], // 作用域链
this:{}
}
VO={
arguments:{...},
foo:<foo reference>,
a:undefined
}
// 执行阶段
VO -> AO
VO = {
arguments: {...},
foo: 1,
bar: <bar reference>,
this: Window
}