1. 程式人生 > 其它 >Js中的預編譯

Js中的預編譯

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物件中尋找,說白了也就是一種就近原則。