这一篇文章我们会针对执行上下文中的变量对象进行详细的解释与阐述,希望在读完这篇文章后,读者朋友们能理解到 JS 中面试毕问的变量提升的知识点。
我们都知道 JS 中有变量提升的一个概念,那么这个过程究竟是怎么发生的呢?我们来看一个例子:
function testVO(name,age){
var a = 1;
var b = function(){
return 2;
}
function c(){
return 3;
}
var c = 4;
}
testVO("leslie",21)
上面这个例子,它的变量究竟是怎么提升的呢,我们可以加上几条测试语句测试一下,
console.log(a);//a是怎么提升的?
console.log(b);//b是怎么提升的?
console.log(c);//c是怎么提升的?
测试结果是:undefined,undefined,function c(){return 3;};
现在就让我们来解释一下为什么会出现这个结果。
执行上下文执行的过程分两个阶段:
-
创建过程:在这个阶段,执行上下文会进行创建变量对象,建立作用域链,以及确定 this 指针的指向。
-
执行过程:进行变量赋值,以及执行其他代码的过程。
那么我们通过这个过程可以知道,变量的提升阶段其实就是创建变量对象的阶段,然后变量对象的赋值阶段也就是执行阶段了,所以我们就知道为什么会有变量提升这个概念了,其实就是由于执行上下文执行的顺序导致的。 那么新的问题又来了,变量究竟是怎么提升的?有没有什么规则呢?
变量对象
变量对象(Variable Object)其实就是一个函数在执行上下文中创建的用来存放该执行上下文中声明的变量的一个对象。 那么变量对象的创建经历了那几步呢?
-
建立 arguments 对象,首先检查当前执行上下文的参数(除开全局上下文,也就是当前函数的 arguments 参数),然后建立属性与属性值的对应关系。
-
函数声明,查找当前执行上下文的所有函数声明(函数有两种声明方式,注意这里是指函数声明,而不是函数表达式声明法),然后建立函数名和函数的引用地址的对应关系。
-
变量声明,查找当前执行上下文的所有变量声明,然后将 undefined 赋值给每一个变量名,如果遇到已经声明过得变量名那么则跳过 undefined 赋值这一步操作。
不知道上面是否能让读者清楚的理解这一个过程,我们再回头看看上一个例子:
function testVO(name,age){
var a = 1;
var b = function(){
return 2;
}
function c(){
return 3;
}
var c = 4;
}
testVO("leslie",21)
首先遇到 testVO("leslie",21), 进入 testVO 这个函数的执行上下文中,我们用 testVOEC 表示,根据上面所说首先进行创建阶段,进行创建变量对象,建立作用域链,以及确定 this 指针的指向这三步,
testVOEC={
VO:{},
actionScope:{}
this:window
}
后面两步我们暂且先不讲,先看 VO 这个变量对象,再根据我们说的 VO 创建的规则我们能够看出来。
VO={
arguments:{
"name":"leslie",
"age":21,
},
c:<c reference>,
a:undefined,
b:undefined
}
可以看出,虽然在函数 c 声明后还有一句 var c = 4; 这一步,但是根据我们的规则 3,如果 c 已经在 VO 变量中,那么就跳过 undefined 赋值这一步操作, 那么执行阶段这一步呢,这时我们引入第二个概念活动对象 AO(Active Object),当当前的执行上下文处于上下文执行栈的栈顶的时候,就开始进入执行阶段,变量对象就变成了活动对象,现在的 VO 就变成了:
AO={
arguments:{
"name":"leslie",
"age":21,
},
c:4,
a:1,
b:<b reference>
}
现在,大家应该对变量提升十分清楚了,就让面试官随意的轰炸把~
注:全局上下文的变量对象和 this 指针都指向 window 对象,只要不关掉页面,全局上下文就会一直存在,其他的上下文也可以一直访问到全局上下文。