2、检查当前上下文的函数声明,按照代码顺序查找,将找到的函数提前声明,如果当前上下文的变量对象没有该函数名属性,则在该变量对象以函数名建立一个属性,属性值则指向该函数所在堆内存地址引用,如果存在,则会被新的引用覆盖掉。
3、检查当前上下文的变量声明,爱去哪找代码顺序查找,将找到的变量提前声明,如果当前上下文的变量对象没有变量名属性,则在该变量对象以变量名建立一个属性,属性值为undefined;如果存在,则忽略该变量声明。
说明:在全局环境中,window对象就是全局执行上下文的变量对象,所有的变量和函数都是window对象的属性方法。
所以函数声明提前和变量声明提升是在创建变量对象中进行的,且函数声明优先级高于变量声明。
下面我们再来分析这个简单代码
function fun(m,n){ var saucxs = 1; function execution(){ console.log(saucxs) } } fun(2,3)这里我们在全局环境中调用fun函数,创建fun的执行上下文,这里暂时不说作用域链以及this指向的问题。
funEC = { //变量对象 VO: { //arguments对象 arguments: { m: undefined, n: undefined, length: 2 }, //execution函数 execution: <execution reference>, //num变量 saucxs: undefined }, //作用域链 scopeChain:[], //this指向 this: window }1、funEC表示fun函数的执行上下文(fun Execution Context 简写为funEC);
2、funEC的变量对象中arguments属性,上面这样写只是为了理解,在浏览器中展示以类数组的方式展示的
3、<execution reference>表示的是execution函数在堆内存地址的引用
说明:创建变量对象发生在预编译阶段,还没有进入到执行阶段,该变量对象都不能访问的,因为此时的变量对象中的变量属性尚未赋值,值仍为undefined,只有在进行执行阶段,变量中的变量属性才进行赋值后,变量对象(Variable Object)转为活动对象(Active Object)后,才能进行访问,这个过程就是VO->AO过程。
3.2.2创建作用域链
作用域链由当前执行环境的变量对象(未进入到执行阶段前)与上层环境的一系列活动对象组成,保证了当前执行还款对符合访问权限的变量和函数有序访问。
理解清楚作用域链可以帮助我们理解js很多问题包括闭包问题等,下面我们结合一个例子来理解一下作用域链。
var num = 30; function test() { var a = 10; function innerTest() { var b = 20; return a + b } innerTest() } test()在上面例子中,当执行到调用innerTest函数,进入到innerTest函数环境。全局执行上下文和test函数执行上下文已进入到执行阶段,innerTest函数执行上下文在预编译阶段创建变量对象,所以他们的活动对象和变量对象分别是AO(global),AO(test)和VO(innerTest),而innerTest的作用域链由当前执行环境的变量对象(未进入到执行阶段前)与上层环境的一系列活动对象组成,如下:
innerTestEC = { //变量对象 VO: {b: undefined}, //作用域链 scopeChain: [VO(innerTest), AO(test), AO(global)], //this指向 this: window }我们这里可以直接使用数组表示作用域链,作用域链的活动对象或者变量对象可以直接理解成作用域。
1、作用域链的第一项永远是当前作用域(当前上下文的变量对象或者活动对象);
2、最后一项永远是全局作用域(全局上下文的活动对象);