探索javascript中函數的執行順序

測試代碼一:

  1. <script language="JavaScript">  
  2. <!--  
  3. function myFn(){  
  4.     alert('Fn1');  
  5. };  
  6.   
  7. myFn();  
  8.   
  9. function myFn(){  
  10.     alert('Fn2');  
  11. };  
  12.   
  13. myFn();  
  14. // -->   
  15. </script>  

 

 

      代碼很簡單,定義兩個同名的函數myFn,然后在不同的地方調用該函數,但執行的結果卻出人意料,
兩次輸出的結果都是Fn2,而不是我們認為的第一次輸出Fn1,第二次輸出Fn2。具體原因這里先不說,接

下來我們繼續測試,看第二段測試代碼:
測試代碼二:

  1. <script type="text/javascript">  
  2. <!--  
  3. function myFn(){  
  4.     alert('Fn1');  
  5. };  
  6.   
  7. myFn();  
  8. // --></mce:script>   
  9. <mce:script type="text/javascript"><!--  
  10. function myFn(){  
  11.     alert('Fn2');  
  12. };  
  13.   
  14. myFn();  
  15. // -->   
  16. </script>  

 

     這里可能大家認為輸出結果跟上次的一樣,錯了,這次第一次輸出Fn1,第二次輸出Fn2.繼續測試,
測試代碼三:

  1. <script language="JavaScript">  
  2. <!--  
  3. var myFn = function(){alert('Fn1');};  
  4. myFn();  
  5. myFn = function(){alert('Fn2');};  
  6. myFn();  
  7. // -->   
  8. </script>  

 

     這次輸出的結果是Fn1,Fn2.

測試代碼四:

  1. <script language="JavaScript">  
  2. <!--  
  3. function myFn(){alert('Fn1');};  
  4. myFn();  
  5.   
  6. myFn = function (){alert('Fn2');};  
  7. myFn();  
  8. // -->   
  9. </script>  

 

    輸出Fn1,Fn2.

測試代碼五:

  1. <script language="JavaScript">  
  2. <!--  
  3. var myFn = function(){alert('Fn1');};  
  4. myFn();  
  5. function myFn(){alert('Fn2');};  
  6. myFn();  
  7. // -->   
  8. </script>  

 

 

輸出Fn1, Fn1

測試代碼六:

  1. <script language="JavaScript">  
  2. <!--  
  3. myFn();  
  4. var myFn = function(){alert('Fn1');};  
  5. myFn();  
  6.   
  7. function myFn(){alert('Fn2');};  
  8. myFn();  
  9. // -->   
  10. </script>  

 

輸出結果是Fn2,Fn1,Fn1

測試代碼七:

  1. <script language="JavaScript">  
  2. <!--  
  3. myFn();  
  4. function myFn(){alert('Fn1');};  
  5. // -->   
  6. </script>  

 

輸出Fn1,而不是未定義的函數

測試代碼八:

  1. <script language="JavaScript">  
  2. <!--  
  3. myFn();  
  4. var myFn = function(){alert('Fn1');};  
  5. // -->   
  6. </script>  

 

則提示缺少對象,也就是函數沒定義。

測試代碼九:

  1. <script language="JavaScript">  
  2. <!--  
  3. myFn();  
  4. // -->   
  5. </script>  
  6. <script type="text/javascript">  
  7. <!--  
  8. function myFn(){alert('Fn1');};  
  9. // -->   
  10. </script>  

 

則提示缺少對象,也就是函數沒定義。

   從上面的測試例子中,我們可以發現javascript具有類似“預編譯”(或者有人稱為“預解釋”)的

特點,從這點看,javascript確實有點像傳統的編譯型語言,比如c,c++等。但javascript跟這種語言又

有根本上的區別,在javascript中,這種預編譯的特性并不是對所有的js代碼進行的,從上面的測試例

子中,我們可以發現,把myFn的定義分別放到不同的script塊中進行調用的時候,就會提示對象未定義

,從這點看,javascript的“預編譯”特性只是對屬于同一塊(即包含在同一個<script></script>塊)中

的代碼有效。
  其實,在javascript的執行過程中,js引擎掃描每一script塊的代碼,把里面的各種函數定義都抽出

來進行“預編譯”,注意,這里說的是函數定義而不是函數賦值,或者說是定義式的函數,那什么是定

義式的函數呢,如下的形式就是:
function myFn(){
 //Code goes here
};
編譯完成后,就根據script塊中的語句從上到下,從左到右進行執行。根據這樣的解釋,上面輸出的各

種結果也就很清楚了。
測試代碼一:
首先js引擎掃描該script塊中的函數定義(注意這個時候還沒開始執行代碼),發現有定義式函數

function myFn(){}有兩處,由于名字是一樣的,編譯后就合成了一個myFn函數,后面的定義覆蓋了前面

的定義,所以在函數執行之前就只有一個編譯的函數myFn,并且其定義是后面的那個,因此真正到執行代

碼的時候,也就是第一次調用myFn(),輸出的當然是Fn2,第二次執行myFn同樣輸出Fn2.

測試代碼二:
由于javascript的塊編譯特性,因此分成在不同的塊中的代碼是分開編譯的,所以第一個script塊編譯

后的myFn函數并沒有被第二塊的myFn覆蓋,因此第一個執行myFn輸出的是Fn1,同樣第二塊輸出的是Fn2.
需要重點指出的是var myFn = function(){};不是定義式函數聲明,而是賦值語句,把一個函數對象賦

值給一個變量,賦值語句的執行時機晚于編譯時刻,定義式函數是在執行語句之前就完成了的,而賦值

語句要到執行的時候才進行。通過這樣的說明,就可以很清楚的解釋
測試代碼七為什么不是提示函數未定義,而是輸出Fn1,而測試代碼八則提示對象未定義的原因了,因為

測試代碼在執行myFn之前,已經優先執行了定義式函數的編譯,也就是說,myFn已經是一個定義了的函

數,因此到執行時候當然能夠正常執行,而測試代碼并沒有定義式函數,在執行代碼之前是沒有任何已

經編譯好的函數定義的,而到執行myFn()的時候當然提示函數沒定義,接下來才執行函數賦值,把一個

函數賦值給一個myFn變量,這時候如果調用myFn就可以了。