JS预解析
一 , 定义
js会在执行前把var ,function等关键字提取至内存中,再逐行运行代码.
二 , 来个栗子说明一切
alert(a); //function a(){alert(10);}
var a = 1;
alert(a); //1
function a() {
alert(5);
}
var a = 3;
alert(a); //3
function a() {
alert(10);
}
alert(a); //3
a(); //Uncaught TypeError: a is not a function
预解析
当js解析器解析到var
和function
关键字或者参数,就把 var
function
后面的变量名提至内存,var
后的赋值undefined,function
后的保存整个函数,于是有
第一个关键字得到:a = undefined
第二个关键字得到:a = function a(){alert(5);}//当预解析中出现重名的变量和函数,舍去变量,保留函数.所以,a更新为function a(){alert(5);}
第三个关键字得到:a = undefined //道理和上一句注释一样,所以a不变,还是function a(){alert(5);}
第四个关键字得到:a = function a(){alert(10);}//当出现两个重名的同级函数,遵循上下文优先级,后来的会把前面的覆盖掉,所以a更新为a = function a(){alert(10);}
至此,预解析结束,保存在内存中的a
是function a(){alert(10);}
开始执行:
第一行代码alert(a)
;直接alert
内存中的a
,也就是function a(){alert(10);}
第二行代码var a = 1
;这个一个表达式,可以修改预解析的值,于是把内存中a
的值改为1.
第三行代码alert(a)
;直接alert
内存中的a
,这时内存中的a
已经被第二行代码更改,此时a = 1
,于是弹出1.
第四行代码function a(){alert(5);}
;这是一个函数声明,但是没有调用,所以什么都不改变,直接跳到下一行.
第五行代码var a = 3
;这同样是个表达式,再次修改预解析值,于是把a
的值改为3.
第六行代码alert(a)
;直接alert
内存中的a
,这时内存中的a
的值已经是3了,所以直接弹出3.
下面的情况类似,不再赘述.
最后一行代码a()
;此时a = 3
,它已经不是一个函数了,所以就会直接报错.
三 , 函数的预解析和运行
第一个例子
var a = 1;
function fn1(){
alert(a); //undefined
var a = 2;
}
fn1();
alert(a); //1
预解析
a = undefined
fn1 = function fn1(){
alert(a);
var a = 2;
}
解析完毕,内存中存入a
和fn1
两个值
开始执行
第一行代码:将a
的值改为1.
第二行代码:一个函数声明,跳过.
第三行代码:执行一个函数,于是进入这个函数(域),开始预解析和运行.
首先预解析:得到a = undefined;
开始执行:第一行代码:输出a,预解析a = undefined.即输出undefined.
第二行代码:更改为a = 2;
第四行代码:输出a,a在它所执行的域里面值是1,所以输出1.
第二个例子
var a = 1;
function fn1(){
alert(a); //1
a = 2;
}
fn1();
alert(a); //2
预解析
与第一个例子得到同样的结果
开始执行
第一第二行代码与例子一相同
第三行代码:执行一个函数,于是进入这个函数(域),开始预解析和运行.
首先预解析:什么也没有,于是开始执行.
开始执行:第一行代码:输出a,但是当前作用域并没有a,于是它就到它的父作用域中去寻找,父作用域中有a = 1,于是输出1.
第二行代码:更改a,同样的,去父作用域中寻找a,找到a,于是把父作用域中的a的值改为2.
第四行代码:输出a,a已经被刚才的函数改为2,所以输出2.
第三个例子
var a = 1;
function fn1(a){
alert(a); //undefined
a = 2;
}
fn1();
alert(a); //1
前面的和前两个例子一样,就不赘述了.
第三行代码:执行一个函数,于是进入这个函数(域),开始预解析和运行.
首先预解析:解析到一个参数a,但是fn1并没有传入参数的值.所以相当于var a;解析完毕得到a = undefined;
开始执行:第一行代码:输出a,在当前域中,已经有a,值为undefined,所以直接输出undefined.
第二行代码:更改a,但是当前域中已经有a了,所以它就近更改自己作用域里的a,而不是去更改父作用域的.
第四行代码:输出a,a是当前作用域的a,而且它已经有值,所以直接输出1.
第四个例子
var a = 1;
function fn1(a){
alert(a); //1
a = 2;
}
fn1(a);
alert(a); //1
第三行代码:执行一个函数,于是进入这个函数(域),开始预解析和运行.
首先预解析:解析到一个参数a,相当于var a,有参数传入,即var a = a(这个a是全局变量,即父元素的a),而父元素a = 1,解析完毕得到a = undefined.
开始执行:第一行代码:输出a,在当前域中,已经有a,值为1,所以直接输出1.
第二行代码:更改a,但是当前域中已经有a了,所以它就近更改自己作用域里的a,而不是去更改父作用域的.所以是把自己作用域的a改为2.
第四行代码:输出a,a是全局作用域的a,而且它已经有值而且一直没有改变过,所以直接输出1.