Js中的預編譯
1.預編譯什麼時候發生
預編譯分為全域性預編譯和區域性預編譯,全域性預編譯發生在頁面載入完成時執行,而區域性預編譯發生在函式執行的前一刻。
預編譯階段發生變數宣告和函式宣告,沒有初始化行為(賦值),匿名函式不參與預編譯 。只有在解釋執行階段才會進行變數初始化 。
2.預編譯前奏
一切宣告的全域性變數和未經宣告的變數,全歸window所有。
下面這個函式裡面只有一個連等的操作,賦值操作都是自右向左的,而b是未經宣告的變數,所以它是歸window的,我們可以直接使用window.b去使用它。
function test(){ // 這裡的b是未經宣告的變數,所以是歸window所有的。 var a = b = 110; console.log(a,b); } test(); console.log(a,b);//報錯
3.預編譯步驟
首先JavaScript的執行過程會先掃描一下整體語法語句,如果存在邏輯錯誤或者語法錯誤,那麼直接報錯,程式停止執行,沒有錯誤的話,開始從上到下解釋一行執行一行。
1.區域性編譯的步驟
- 建立AO物件(Activation Object)執行期上下文。
- 找形參和變數宣告,將變數和形參名作為AO屬性名,值為undefined
- 將實參值和形參統一。
- 在函式體裡面找函式宣告,值賦予函式體。
2.全域性編譯的步驟
- 建立GO物件(Global Object)全域性物件。
- 找變數宣告,將變數名作為GO屬性名,值為undefined
- 查詢函式宣告,作為GO屬性,值賦予函式體
由於全域性中沒有引數的的概念,所以省去了實參形參相統一這一步。
關於AO物件的例子:
// 函式 function fn(a){ console.log(a); // 變數宣告+變數賦值(只提升變數宣告,不提升變數賦值) var a = 123; console.log(a); // 函式宣告 function a(){}; console.log(a); // 函式表示式 var b = function(){}; console.log(b); // 函式 function d(){}; } //呼叫函式 fn(1);
1.預編譯第1步:建立AO物件
AO{ }
2.預編譯第2步:找形參和變數宣告,將形參名和變數名作為AO物件的屬性名
AO{ a : undefined, b : undefined }
3.預編譯第3步:將實參值和形參統一
AO{ a : 1, b : function(){...} }
4.預編譯第4步:在函式體裡面找函式宣告,值賦予函式體
AO{ a : function a(){...}, b : undefined, d : function d(){...} }
最後輸出結果:
// 函式 function fn(a){ console.log(a); //根據AO物件中的資料第一個列印的是:fn() // 變數宣告+變數賦值(只提升變數宣告,不提升變數賦值) var a = 123; // 執行到這時,由於變數賦值是不提升的,所以函式被123覆蓋了 console.log(a); // 123 // 函式宣告 function a(){}; // 這裡被提升上去了,可以忽略 console.log(a); // 123 // 函式表示式 var b = function(){}; console.log(b); // 根據AO物件中的資料:fn() // 函式 function d(){}; } //呼叫函式 fn(1);
函式執行完畢,銷燬AO物件。
關於GO物件的例子:
global = 100; function test(){ console.log(global); var global = 200; console.log(global); var global = 300; } test(); var global;
1.全域性預編譯第1步:建立GO物件
GO{ }
2.全域性預編譯第2步:找變數宣告,將變數名作為GO屬性名,值為undefined
GO{ global:undefined }
3.全域性預編譯第3步:查詢函式宣告,作為GO屬性,值賦予函式體
GO{ global:undefined }
4.區域性預編譯第1步:建立AO物件
AO{ }
5.區域性預編譯第2步:找形參和變數宣告,將形參名和變數名作為AO物件的屬性名
AO{ global: undefined }
6.區域性預編譯第3步:將實參值和形參統一(此函式沒有形參)
AO{ global: undefined }
7.區域性預編譯第4步:在函式體裡面找函式宣告,值賦予函式體。(此函式內沒有函式宣告)
AO{ global: undefined }
最後的結果:
global = 100; function test(){ console.log(global); // 根據AO物件中的資料:undefined var global = 200; // 執行到這時,200覆蓋了undefined console.log(global); // 200 var global = 300; } test(); var global;
關於GO物件和AO物件,它們倆是一個種鏈式關係,就拿上面的這個例子來說吧,如果在函式體的內部沒有定義global變數,這也意味著AO物件中將有這個global這個屬性。那如果沒有會怎麼辦?它會去GO物件中尋找,說白了也就是一種就近原則。