在很多面试题中,经常会看到关于变量提升,还有函数提升的题目,所以我就写一篇自己理解之后的随笔,方便之后的查阅和复习。
首先举个例子
foo();//undefined
function foo(){
console.log(a);//undefined
var a = 10;
}
上面的例子中,foo()
函数的声明在调用之后,但是还是会输出函数中的结果。在foo()
函数的内部,变量a
的声明之前就调用了,但是系统会输出undefined
,而不会报错,这里面就涉及到了变量提升
还有函数提升
,为什么会出现这样的情况呢,先来了解几个重要的概念,就可以解开这个谜底了。
一、什么是执行上下文
每次当控制器转到可执行代码的时候,就会进入一个可执行上下文。执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。
一个执行上下文的生命周期
从图中可以看出,一个执行上下文的生命周期包括
创建
和执行
两个阶段。1、第一个阶段中做的工作有:
- 生成变量对象
- 建立作用域链
- 确定this的指向
2、第二个阶段中做的工作有:
- 变量赋值
- 函数引用
- 执行其他代码
到这里还不能解析为什么会有变量提升和函数提升的过程,那么我们接下来再深入执行上下文
中的创建
阶段中的生成变量对象的过程
什么是变量对象
在很多面试题中,会考到变量对象和活动对象的区别,其实活动对象和变量对象是同一个对象,只是处于执行上下文的不同生命周期而已,活动对象处于执行阶段。
从图中可以看出,在生成变量对象的过程中,执行的操作有三步
1、创建arguments对象
2、检查functfen函数声明创建属性
3、检查var变量声明创建属性
所以在了解完执行上下文和变量对象后,就可以分析一下我们的问题了。
当我们执行第一句话的时候foo()
,javascript引擎会创建一个执行上下文testEc
,并且生成一个变量对象VO
,首先创建argument对象
,然后检查function
函数声明并且创建一个属性,所以就生成了如下的vo对象
创建过程
testEC = {
// 变量对象
VO: {},
scopeChain: {},
this: {}
}
// 因为本文暂时不详细解释作用域链和this,所以把变量对象专门提出来说明
// VO 为 Variable Object的缩写,即变量对象
VO = {
arguments: {...}, //注:在浏览器的展示中,函数的参数可能并不是放在arguments对象中,这里为了方便理解,我做了这样的处理
foo: <foo reference> // 表示foo的地址引用
a: undefined
}