探索javascript中函式的執行順序
先來第一段測試程式碼,如下所示:
測試程式碼一:
- <script language="JavaScript">
- <!--
- function myFn(){
- alert('Fn1');
- };
-
- myFn();
- function myFn(){
- alert('Fn2');
- };
- myFn();
- // -->
- </script>
程式碼很簡單,定義兩個同名的函式myFn,然後在不同的地方呼叫該函式,但執行的結果卻出人意料,
兩次輸出的結果都是Fn2,而不是我們認為的第一次輸出Fn1,第二次輸出Fn2。具體原因這裡先不說,接
下來我們繼續測試,看第二段測試程式碼:
測試程式碼二:
- <script type="text/javascript">
- <!--
- function myFn(){
- alert('Fn1');
- };
-
- myFn();
- // --></mce:script>
- <mce:script type="text/javascript"><!--
- function myFn(){
- alert('Fn2');
- };
- myFn();
- // -->
- </script>
這裡可能大家認為輸出結果跟上次的一樣,錯了,這次第一次輸出Fn1,第二次輸出Fn2.繼續測試,
測試程式碼三:
- <script language="JavaScript">
- <!--
- var myFn = function(){alert('Fn1');};
- myFn();
- myFn = function(){alert('Fn2');};
- myFn();
- // -->
- </script>
這次輸出的結果是Fn1,Fn2.
測試程式碼四:
[javascript] view plain copy- <script language="JavaScript">
- <!--
- function myFn(){alert('Fn1');};
- myFn();
- myFn = function (){alert('Fn2');};
- myFn();
- // -->
- </script>
輸出Fn1,Fn2.
測試程式碼五:
[javascript] view plain copy- <script language="JavaScript">
- <!--
- var myFn = function(){alert('Fn1');};
- myFn();
- function myFn(){alert('Fn2');};
- myFn();
- // -->
- </script>
輸出Fn1, Fn1
測試程式碼六:
[javascript] view plain copy- <script language="JavaScript">
- <!--
- myFn();
- var myFn = function(){alert('Fn1');};
- myFn();
- function myFn(){alert('Fn2');};
- myFn();
- // -->
- </script>
輸出結果是Fn2,Fn1,Fn1
測試程式碼七:
[javascript] view plain copy- <script language="JavaScript">
- <!--
- myFn();
- function myFn(){alert('Fn1');};
- // -->
- </script>
輸出Fn1,而不是未定義的函式
測試程式碼八:
[javascript] view plain copy- <script language="JavaScript">
- <!--
- myFn();
- var myFn = function(){alert('Fn1');};
- // -->
- </script>
則提示缺少物件,也就是函式沒定義。
測試程式碼九:
[javascript] view plain copy- <script language="JavaScript">
- <!--
- myFn();
- // -->
- </script>
- <script type="text/javascript">
- <!--
- function myFn(){alert('Fn1');};
- // -->
- </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就可以了。 轉自:http://blog.csdn.net/tenfyguo/article/details/4525957