欢迎大家关注我的 github
前言
当调用一个函数的时候,一个新的执行上下文就会被创建,一个执行上下文的声明周期可以分为两个阶段:
- 创建阶段
- 代码执行阶段

变量对象
变量对象的创建过程:(解释变量提升)
- 建立arguments对象,检查当前上下文的参数,建立该对象下的属性和属性值
- 检查当前上下文的函数声明,也就是function关键字声明的函数,在对象变量中以函数名创建一个属性,属性值指向该函数所在内存地址的引用。如果函数名属性已存在,该属性将会被新的引用覆盖
- 检查当前上下文的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已存在,则会跳过,原属性值不改变。

函数的声明比变量声明的优先级更高一点
变量对象和活动对象的区别:
在执行上下文的创建阶段,变量对象的属性都是不可访问的,进入执行上下文阶段,变量对象变成了活动对象,属性都可以访问,并进行执行阶段的操作,他们都是同一个对象,只是处在了执行上下文的不同生命周期。
例子:
function test1() {
console.log(a)
console.log(foo())
var a = 1;
function foo() {
return 2;
}
}
test1()
// undefined
// 2
创建的过程:
test1EC = {
VO(变量对象): {}, // 创建变量对象
scopeFun: {}, // 创建作用域链
this: {} // 确定this指向
}
VO= {
arguments: {...},
foo: (函数foo的地址引用),
a: undefined
}
当进入执行阶段的时候
AO = {
arguments: {...},
foo: (函数foo的地址引用),
a: 1
}
例子:
function test() {
console.log(foo);
console.log(bar);
var foo = 'Hello';
console.log(foo);
var bar = function () {
return 'world';
}
console.log(bar)
function foo() {
return 'hello';
}
}
结果:
test()
foo() {
return 'hello';
}
// undefined
// Hello
// function() {
return 'world'
}
在创建的过程中:
VO = {
arguments: {},
foo: foo函数的地址,
bar: undefined
}
进入执行阶段(还未执行到var foo = 'Hello'时):
AO = {
arguments: {},
foo: foo函数的地址,
bar: undefined
}
进入执行阶段(执行到后面的时候):
AO = {
arguments: {},
foo: 'Hello',
bar: 匿名函数的地址
}
由于变量对象在创建阶段的时候,函数声明foo一开始是foo函数的地址,后来被同名属性覆盖,所以后来变成了'Hello',而bar是变量声明,所以得等到执行的时候才会有值

参考资料:
http://www.cnblogs.com/lsgxeva/p/7976034.html