js预解析

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解析器解析到varfunction关键字或者参数,就把 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);}

至此,预解析结束,保存在内存中的afunction 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;
}

解析完毕,内存中存入afn1两个值

开始执行

第一行代码:将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.

posted @ 2017-09-19 14:56  puss_in_art  阅读(253)  评论(0编辑  收藏  举报