执行上下文非常重要,其中涉及了变量对象、作用域链、 this,闭包 等许多重要但并不那么容易搞清楚的概念,这些概念有助于我们真正理解 JavaScript 代码的运行机制,所以执行上下文牵扯的东西太多了,非常重要,非常重要,非常重要!!!
JavaScript代码在执行时
,会进入一个执行上下文
中。执行上下文可以理解为当前代码的运行环境
JavaScript中的运行环境主要包括以下三种情况:
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
}