JS的預解析過程:
1,預解析
2,再逐行解讀代碼,
實例:
----------------------------
<script>
var name="xm";
var age=18;
function fn(argument){
console.log(name);//輸出未定義:undefined
var name="xh";
var age=12;
}
</script>
----------------------------
解析:
全局作用域,局部作用域,都是通過以下兩個步驟進行預解析的。
1,先讀取有var 的變量(沒有使用var的變量是不會被預解析的),給賦值為:undefined。如果兩個變量重名,並不影響預解析的過程,就寫一個變量就行,因為都將變量賦值為undefined(在逐行讀取時,只是不同的賦值而已。),如果有函數名和變量重名,那就直接去掉變量,不進行解析。如果函數中存在參數,那么參數也一樣使用var進行解析。如:var argument=undefined;
2,再讀取function后面的函數---fn,如果有多個函數名重復,那么取最后面一個函數進行聲明。
上面實例有兩個作用域,一個是window變量對象的全局作用域,一個是fn變量對象的局部作用域。
預解析過程如下:
1,window:
var name=undefined;
var age=undefined;
function fn(argument){
console.log(name);
var name="xh";
var age=12;
}
2,fn:
var name=undefined;
var age=undefined;
var argument=undefined;
預解析就完成了。然后就是逐行解讀代碼。
通過逐行解讀代碼進行賦值,改變變量屬性值。如果遇到函數,那么就直接跳過,因為預解析時已經聲明過。
所以,上面實例:console.log(name); 輸出為: undefined。
實例1:
<script>
console.log(fn);
function fn(){}; //預解析:function fn(){};
</script>
輸出結果:function fn(){};
實例2:
<script>
console.log(fn);
var fn=function(){}; //預解析:var fn=undefined;
</script>
輸出結果:undefined 未定義。
實例3:
<script>
console.log(a);//所以這里a是沒有定義,運行就報錯。運行未定義變量報錯,運行未定義屬性返回undefined
a=1;//預解析沒有變量。
</script>
輸出結果:這里的a沒有定義,所以報錯,未定義。
-----------------------
<script>
console.log(a); //預解析中只剩下一個函數a,所以這里輸出是整個函數a。 函數里面的表達式不進行預解析
var a=1; //聲明一個變量 a 進行賦值,所以跟預解析沒關系
console.log(a);//所以這里輸出為:1。
function a(){ //兩個函數重名只保留最后一個,所以這個函數不進行解析,在逐行解讀代碼時,沒有進行解析的代碼也是跳過的。
console.log(2);
};
console.log(a);//函數被當成變量賦值為:1,a就是一個變量。所以這里輸出為:1。
var a=3; //聲明一個變量 a 進行賦值,所以跟預解析沒關系
console.log(a);//這里輸出為:3。
function a(){ //被預解析的函數就直接跳過不進行解析,因為預解析時,已經聲明過了。
console.log(4);
};
console.log(a);//函數被當成變量賦值為:3。所以這里輸出為:3。
a(); //函數被當成變量賦值為:3。所以這里的a為變量,變量不可以當做函數執行。
</script>
輸出結果:
function a(){console.log(4);}
1
1
3
3
報錯(a=3,不能當函數執行);
預解析:
//var a=undefined; 變量a與函數 a 重名,所以變量不進行預解析。
//兩個函數重名,只對最后一個進行預解析。
function a(){
console.log(4);
}
-----------------------
預解析是一個標簽執行完畢並且逐行解讀代碼完成,才執行第二個標簽的預解析和逐行代碼解讀:
以下是存在兩個script的情況,進行預解析:
<script>
console.log(a);//這個標簽預解析后沒有變量需要聲明,再執行console.log(a); a是沒有聲明的變量,所以報錯。
</script>
<script>
var a=1;
</script>
輸出結果為:報錯。第一個標簽的a沒有聲明。
預解析是一個標簽執行完畢並且逐行解讀代碼完成,才執行第二個標簽的預解析和逐行代碼解讀:
以下是存在兩個script的情況,進行預解析:
<script>
var a=1;//預解析:var a=undefined; 再被復制為1。執行完畢,再進行下面一個標簽預解析,在逐行解讀代碼。
</script>
<script>
console.log(a); //所以這里輸出為:1。
</script>
輸出結果:1。
-----------------------
<script>
var a=1; //解析:var a=undefined;
function fn(a){ //解析:var a=undefined;
console.log(a); //執行完fn(a)后,輸出1。因為這里的a變成全局變量了。重點。
a=2;
}
fn(a); //這里的a是全局變量,因為局部變量不能再全局中使用,只有全局變量才能在局部中使用。所以這個a=1。
console.log(a); //局部變量不能再全局中使用,只有全局變量才能在局部中使用。所以這里輸出1。
</script>
輸出為:1 , 1
預解析:
window變量對象的屬性以及方法:
var a=undefined;
fn(a){console.log(a); a=2;};
fn變量對象的屬性以及方法:
var a=undefined //這里的 a 是參數。
------------------------
不要在代碼塊中聲明函數,因為這樣有些老的狐火瀏覽器無法進行正確的預解析。
if(){
function fn_name(argument){.....body......}
}
for(){
function fn_name(){.....body......}
}
=================總結=====================
上圖:
基本變量復制(這里應該是賦值)時,創建一個副本:
var name="xm";
name="xh";//基本類型賦值,跟上面一個值無關。即:兩個值,在棧內存中開辟了兩個位置存放數據。
引用類型賦值的其實是指針:
var xh={sex:"female" age:18};
var xm=xh;//把xh的引用賦值給xm。還是指向同一個堆內存空間,所以兩個值和類型都想相等(即同一個引用),即相等。
基本數據類型和引用數據類型傳遞參數。
兩種類型都一樣:都是按值傳遞參數,並不是按引用傳遞參數。直接值代替參數。