js預解析
js的預解析:
為什麼我們用var定義一個變數a,在這行程式碼之前列印a卻不報錯,而是得到undefined?又或者函式為什麼可以隨意放置,在哪裡呼叫都可以應用,這是什麼原理?
其實這樣的情況,都是因為JavaScript中有一種機制,就是:“預解析機制”。
1.1 預解析原理
簡單來說,預解析就是——在當前作用域下,js在執行之前,會把帶有var和function關鍵字宣告的變數先宣告,並在記憶體中安排好。然後再從上至下解析js語句。
var 預解析
js 在正常解析之前,會快速的把 script (或者 funciton )中的 var 宣告及宣告的名字,提升到程式碼塊(作用域)的最前面。
function 預解析
js 在正常解析之前,會快速的把 script (或者 funciton )中的 function 及內容提升到程式碼塊(作用域)的最前面,跟在 var 宣告之後。
1.2 var宣告
通過var宣告的變數,進行預解析的時候:先宣告變數,不管變數有沒有賦值,宣告時都賦值為undefined。
事例:
console.log(a); //undefined
var a = 1;
console.log(b); //undefined
var b = function(){}//注意,這是個函式表示式
事例:
第1步:
console.log(a);// undefined,此時讀取變數a
var a = 10;
//解釋:在執行程式碼之前,js會預解析程式碼,程式碼中如果有變數宣告和函式宣告的話,那麼便將變數宣告和函式宣告置頂;
也就是說,以上程式碼在js執行中,是如下執行的:
var a;//變數宣告置頂設定,但不賦值
console.log(a); //undefined
a = 10;//變數在此處賦值。
第2步:
console.log(a); //undefined
console.log(b); //undefined
var a = 10;
var b = function(){}//同樣是var宣告的函式進行置頂設定,但是不賦值;
第3步:
var a = 10;
var a = function(){};
console.log(a); //function (){}
相當於:
var a = 10;
a = function(){};
console.log(a); //function (){}
var定義的a,是同一個a,不會產生不同的變數,只是讓a改變了值,由數值變成了函式。
反之亦然,
var a = function(){};
var a = 10;
console.log(a); //10
var宣告按照賦值順序執行,同名的var宣告,後者的宣告不會產生新變數,只是值會覆蓋前者。
1.3 function宣告
js 在正常解析之前,會快速的把 script (或者 funciton )中的 function 及內容提升到程式碼塊(作用域)的最前面,跟在 var 宣告之後。
function進行預解析的時候,不僅是宣告而且還定義了函式體,在記憶體中會開闢一塊記憶體空間,儲存的是函式體的字串,但是程式碼不會執行。只有函式呼叫以後才執行。
事例:
第1步
console.log(a); //列印結果不是a,而是函式a()的方法體!證明函式a跟在var宣告之後被宣告的。
var a = 10;
function a(){return 100;};
其實相當於:
var a;
function a(){return 100;};
a = 10;
第2步
var a = 10;
function a(){return 100;};
console.log(a); //10
解釋請看上一步的程式碼解釋。
注意:
(1) 同名的var宣告和同名的函式宣告(不是函式表示式,用function宣告的函式),不管二者書寫先後順序,函式宣告會覆蓋掉var宣告的變數(因為它的置頂順序低於var宣告);
(2) 同名的var宣告,後者會被忽略;
(3) 同名的函式宣告,後者會覆蓋前面的(這只是函式宣告按自上而下的順序執行罷了)。
課堂練習,思考一下結果:
var a = 10;
function a(){return 100;};
var a = function (){return 10000;};
console.log(a);
注意問題:
(1) 預解析,不會超出script標籤,前面訪問不到後面的定義
<script>console.log(a) // 報錯</script>
<script>function a(){}</script>
(2) 後面script標籤中可以訪問呢前面script標籤中的js,因為js是從上至下執行的。
(3) 函式內部同名變數的宣告高於傳入的同名引數
var a = 10;
function fn(a){
var a = 20;
console.log(a);//20
}
fn(a);
(4) 函式內參數的宣告高於外部同名變數的宣告。
var a = 10;
function fn(a){
console.log(a);//100
}
fn(100);
1.4 預解析應用
var a = 1;
function b(){
a = 10;
return;
function a(){
console.log(a);
}
}
b();
console.log(a);
思考,列印結果?
注意:預解析既可置頂提升變數也可以提升函式。
程式碼會按照如下情況執行:
var a;
function b(){//注意,函式也會提升
function a(){//注意,函式也會提升,而不是被return結束掉了。
console.log(a);
}
a = 10;//注意,作用域鏈的應用,它的查詢順序是由內而外,就近原則!所以雖然函式a沒有被呼叫過,但是這裡的賦值,是賦值給b函式中的a函式的!
return;
}
a = 1;
b();
console.log(a);
課堂練習:
①
var a;
function a(){
console.log(10);
}
console.log(a);
②
function a(){
console.log(10);
}
var a;
console.log(a);
③
function a() {
var b = 1;
}
console.log(a);
var a = function() {
var b = 2;
};