天天看點

js頁面加載順序

函數的聲明和調用  

JavaScript是一種描述型腳本語言,由浏覽器進行動态的解析與執行。函數的定義方式大體有以下兩種,浏覽器對于不同的方式有不同的解析順序。 

代碼如下: 

複制代碼代碼如下:

//“定義式”函數定義 

function Fn1(){ 

alert("Hello World!"); 

//“指派式”函數定義 

var Fn2 = function(){ 

alert("Hello wild!"); 

頁面加載過程中,浏覽器會對頁面上或載入的每個js代碼塊(或檔案)進行掃描,如果遇到定義式函數,則進行預處理(類似于C等的編譯),處理完成之後再開始由上至下執行;遇到指派式函數,則隻是将函數賦給一個變量,不進行預處理(類似1中變量必須先定義後引用的原則),待調用到的時候才進行處理。下面舉個簡單的例子: 

代碼如下: 

複制代碼代碼如下:

//“定義式”函數定義 

Fn1(); 

function Fn1(){ 

alert("Hello World!"); 

正常執行,彈出“Hello World!”,浏覽器對Fn1進行了預處理,再從Fn1();開始執行。 

代碼如下: 

複制代碼代碼如下:

//“指派式”函數定義 

Fn2(); 

var Fn2 = function(){ 

alert("Hello wild!"); 

Firebug報錯:Fn2 is not a function,浏覽器未對Fn2進行預處理,依序執行,是以報錯Fn2未定義。 

3. 代碼塊及js檔案的處理  

“代碼塊”是指一對<script type=”text/網頁特效”></script>标簽包裹着的js代碼,檔案就是指檔案啦,廢話:D 

浏覽器對每個塊或檔案進行獨立的掃描,然後對全局的代碼進行順序執行(2中講到了)。是以,在一個塊(檔案)中,函數可以在調用之後進行“定義式”定義;但在兩個塊中,定義函數所在的塊必須在函數被調用的塊之前。 

很繞口,看例子好了: 

代碼如下: 

複制代碼代碼如下:

<script type="text/javascript"> 

Fn(); 

</script> 

<script type="text/javascript"> 

function Fn(){ 

alert("Hello World!"); 

</script> 

// 報錯:Fn is notdefined,兩個塊換過來就對了 

4. 重複定義函數會覆寫前面的定義  

這和變量的重複定義是一樣的,代碼: 

代碼如下: 

複制代碼代碼如下:

function fn(){ 

alert(1); 

function fn(){ 

alert(2); 

fn(); 

// 彈出:“2” 

如果是這樣呢: 

代碼如下: 

複制代碼代碼如下:

fn(); 

function fn(){ 

alert(1); 

function fn(){ 

alert(2); 

// 還是彈出:“2” 

還是彈出“2”,為什麼?2都講了好吧… 

5. body的onload函數與body内部函數的執行  

body内部的函數會先于onload的函數執行,測試代碼: 

代碼如下: 

複制代碼代碼如下:

//html head... 

<script type="text/javascript"> 

function fnOnLoad(){ 

alert("I am outside the Wall!"); 

</script> 

<body οnlοad="fnOnLoad();"> 

<script type="text/javascript"> 

alert("I am inside the Wall.."); 

</script> 

</body> 

//先彈出“I am inside the Wall..”; 

//後彈出“I am outside the Wall!” 

body的onload事件觸發條件是body内容加載完成,而body中的js代碼會在這一事件觸發之前運作(為什麼呢?6告訴你..) 

6. JavaScript是多線程or單線程?  

嚴格來說,JavaScript是沒有多線程概念的,所有的程式都是“單線程”依次執行的。 

舉個不太恰當的例子: 

代碼如下: 

複制代碼代碼如下:

function fn1(){ 

var sum = 0; 

for(var ind=0; ind<1000; ind++) { 

sum += ind; 

alert("答案是"+sum); 

function fn2(){ 

alert("早知道了,我就是不說"); 

fn1(); 

fn2(); 

//先彈出:“答案是499500”, 

//後彈出:“早知道了,我就是不說” 

那你肯定要問:那延時執行、Ajax異步加載,不是多線程的嗎?沒錯,下面這樣的程式确實看起來像“多線程”: 

代碼如下: 

複制代碼代碼如下:

function fn1(){ 

setTimeout(function(){ 

alert("我先調用") 

},1000); 

function fn2(){ 

alert("我後調用"); 

fn1(); 

fn2(); 

// 先彈出:“我後調用”, 

// 1秒後彈出:“我先調用” 

看上去,fn2()和延時程式是分兩個過程再走,但其實,這是JavaScript中的“回調”機制在起作用,類似于作業系統中的“中斷和響應” —— 延時程式設定一個“中斷”,然後執行fn2(),待1000毫秒時間到後,再回調執行fn1()。 

同樣,5中body的onload事件調用的函數,也是利用了回調機制——body加載完成之後,回調執行fnOnLoad()函數。 

Ajax請求中的資料處理函數也是一樣的道理。 

關于JavaScript線程問題的更深入讨論,看這篇 javascript中的線程之我見,以及infoQ上的 JavaScript多線程程式設計簡介。 

困了,再說一下回調函數吧。 

7. 回調函數  

回調函數是幹嘛用的?就是回調執行的函數嘛,又廢話:D 

如6所說,最常見的回調就是onclick、onmouseo教程ver、onmousedown、onload等等浏覽器事件的調用函數;還有Ajax異步請求資料的處理函數;setTimeOut延時執行、setInterval循環執行的函數等。 

幹脆我們寫一個純粹的回調函數玩: 

代碼如下: 

複制代碼代碼如下:

function onBack(num){ 

alert("姗姗我來遲了"); 

// 執行num個耳光 

function dating(hours, callBack){ 

var SP= 0; // SP,憤怒值 

//女豬腳在雪裡站了hours個鐘頭 

//循環開始.. 

SP ++; 

//循環結束... 

callBack(SP); 

dating(1, onBack); 

js