给你一段这样的代码。
function fun() {
console.log(a);
var a= 1;
function a(){
return 2;
}
}
fun();
经过代码运行可得:
为何会出现这种结果,这就与执行上下文有关。
执行上下文
基本概念
当调用一个函数时,一个执行上下文就会被创建(激活),执行上下文可以理解成一个运行环境,它会形成一个作用域。运行环境一般可以分为全局环境、函数环境、eval(基本可忽略),而js引擎会以栈的形式管理这些上下文,全局上下文始终处于栈的最底部,而栈顶则是当前正在执行的上下文。
执行上下文的两个周期
文章开始的代码结果与上下文的周期有关。
上下文可以分为创建阶段和代码执行阶段。
创建阶段
在这个阶段,执行上下文会创建变量对象,建立作用域链,以及确定this的指向。
本文主要讲解变量对象(VO variable object)。变量对象主要会经历以下几个过程。
1.建立argument,检测该对象下的参数,建立属性和属性值。
2.检查当前上下文中的函数声明,也就是function关键字声明的函数。以函数名建立属性,属性值指向该函数指向内存地址的引用。若函数名的属性已经存在,则属性将被新的引用所覆盖。
3.检查当前上下文中的变量对象,每找到一个变量声明,就以对象名建立属性,属性值为undefined。若变量名的属性已经存在,为防止函数被修改为undefined,直接跳过,原属性值不改变。
所以文章开头代码在这一阶段后,会建立如下变量对象。
VO = {
arguments: {...},
a: <a reference> //表示地址的应用
}
注意: 在找到a变量声明时,变量对象中已有a属性,所以直接到过,并不会建立a: undefined。
代码执行阶段
在这个阶段,主要会做变量赋值,函数应用,以及其他代码执行。
所以文章开头代码的实际执行顺序如下。
function fun() {
function a(){
return 2;
}
//var a=undefined 由于已存在a属性,所以将直接跳过,不会进行undefined赋值。
console.log(a);
a= 1;
}
其他
对于变量提升,我们也可以通过执行上下文的生命周期来描述,在创建阶段,检测上下文中的变量声明,若属性中没有该变量名,则会在变量对象中添加 变量名:undefined ,从而就实现了变量提升。