- <script language="JavaScript">
- <!--
- function myFn(){
- alert('Fn1');
- };
- myFn();
- function myFn(){
- alert('Fn2');
- };
- myFn();
- // -->
- </script>
代碼很簡單,定義兩個(gè)同名的函數(shù)myFn,然后在不同的地方調(diào)用該函數(shù),但執(zhí)行的結(jié)果卻出人意料,
兩次輸出的結(jié)果都是Fn2,而不是我們認(rèn)為的第一次輸出Fn1,第二次輸出Fn2。具體原因這里先不說,接
下來我們繼續(xù)測試,看第二段測試代碼:
測試代碼二:
- <script type="text/javascript">
- <!--
- function myFn(){
- alert('Fn1');
- };
- myFn();
- // --></mce:script>
- <mce:script type="text/javascript"><!--
- function myFn(){
- alert('Fn2');
- };
- myFn();
- // -->
- </script>
這里可能大家認(rèn)為輸出結(jié)果跟上次的一樣,錯(cuò)了,這次第一次輸出Fn1,第二次輸出Fn2.繼續(xù)測試,
測試代碼三:
- <script language="JavaScript">
- <!--
- var myFn = function(){alert('Fn1');};
- myFn();
- myFn = function(){alert('Fn2');};
- myFn();
- // -->
- </script>
這次輸出的結(jié)果是Fn1,Fn2.
測試代碼四:
- <script language="JavaScript">
- <!--
- function myFn(){alert('Fn1');};
- myFn();
- myFn = function (){alert('Fn2');};
- myFn();
- // -->
- </script>
輸出Fn1,Fn2.
測試代碼五:
- <script language="JavaScript">
- <!--
- var myFn = function(){alert('Fn1');};
- myFn();
- function myFn(){alert('Fn2');};
- myFn();
- // -->
- </script>
輸出Fn1, Fn1
測試代碼六:
- <script language="JavaScript">
- <!--
- myFn();
- var myFn = function(){alert('Fn1');};
- myFn();
- function myFn(){alert('Fn2');};
- myFn();
- // -->
- </script>
輸出結(jié)果是Fn2,Fn1,Fn1
測試代碼七:
- <script language="JavaScript">
- <!--
- myFn();
- function myFn(){alert('Fn1');};
- // -->
- </script>
輸出Fn1,而不是未定義的函數(shù)
測試代碼八:
- <script language="JavaScript">
- <!--
- myFn();
- var myFn = function(){alert('Fn1');};
- // -->
- </script>
則提示缺少對象,也就是函數(shù)沒定義。
測試代碼九:
- <script language="JavaScript">
- <!--
- myFn();
- // -->
- </script>
- <script type="text/javascript">
- <!--
- function myFn(){alert('Fn1');};
- // -->
- </script>
則提示缺少對象,也就是函數(shù)沒定義。
從上面的測試?yán)又校覀兛梢园l(fā)現(xiàn)javascript具有類似“預(yù)編譯”(或者有人稱為“預(yù)解釋”)的
特點(diǎn),從這點(diǎn)看,javascript確實(shí)有點(diǎn)像傳統(tǒng)的編譯型語言,比如c,c++等。但javascript跟這種語言又
有根本上的區(qū)別,在javascript中,這種預(yù)編譯的特性并不是對所有的js代碼進(jìn)行的,從上面的測試?yán)?/p>
子中,我們可以發(fā)現(xiàn),把myFn的定義分別放到不同的script塊中進(jìn)行調(diào)用的時(shí)候,就會(huì)提示對象未定義
,從這點(diǎn)看,javascript的“預(yù)編譯”特性只是對屬于同一塊(即包含在同一個(gè)<script></script>塊)中
的代碼有效。
其實(shí),在javascript的執(zhí)行過程中,js引擎掃描每一script塊的代碼,把里面的各種函數(shù)定義都抽出
來進(jìn)行“預(yù)編譯”,注意,這里說的是函數(shù)定義而不是函數(shù)賦值,或者說是定義式的函數(shù),那什么是定
義式的函數(shù)呢,如下的形式就是:
function myFn(){
//Code goes here
};
編譯完成后,就根據(jù)script塊中的語句從上到下,從左到右進(jìn)行執(zhí)行。根據(jù)這樣的解釋,上面輸出的各
種結(jié)果也就很清楚了。
測試代碼一:
首先js引擎掃描該script塊中的函數(shù)定義(注意這個(gè)時(shí)候還沒開始執(zhí)行代碼),發(fā)現(xiàn)有定義式函數(shù)
function myFn(){}有兩處,由于名字是一樣的,編譯后就合成了一個(gè)myFn函數(shù),后面的定義覆蓋了前面
的定義,所以在函數(shù)執(zhí)行之前就只有一個(gè)編譯的函數(shù)myFn,并且其定義是后面的那個(gè),因此真正到執(zhí)行代
碼的時(shí)候,也就是第一次調(diào)用myFn(),輸出的當(dāng)然是Fn2,第二次執(zhí)行myFn同樣輸出Fn2.
測試代碼二:
由于javascript的塊編譯特性,因此分成在不同的塊中的代碼是分開編譯的,所以第一個(gè)script塊編譯
后的myFn函數(shù)并沒有被第二塊的myFn覆蓋,因此第一個(gè)執(zhí)行myFn輸出的是Fn1,同樣第二塊輸出的是Fn2.
需要重點(diǎn)指出的是var myFn = function(){};不是定義式函數(shù)聲明,而是賦值語句,把一個(gè)函數(shù)對象賦
值給一個(gè)變量,賦值語句的執(zhí)行時(shí)機(jī)晚于編譯時(shí)刻,定義式函數(shù)是在執(zhí)行語句之前就完成了的,而賦值
語句要到執(zhí)行的時(shí)候才進(jìn)行。通過這樣的說明,就可以很清楚的解釋
測試代碼七為什么不是提示函數(shù)未定義,而是輸出Fn1,而測試代碼八則提示對象未定義的原因了,因?yàn)?/p>
測試代碼在執(zhí)行myFn之前,已經(jīng)優(yōu)先執(zhí)行了定義式函數(shù)的編譯,也就是說,myFn已經(jīng)是一個(gè)定義了的函
數(shù),因此到執(zhí)行時(shí)候當(dāng)然能夠正常執(zhí)行,而測試代碼并沒有定義式函數(shù),在執(zhí)行代碼之前是沒有任何已
經(jīng)編譯好的函數(shù)定義的,而到執(zhí)行myFn()的時(shí)候當(dāng)然提示函數(shù)沒定義,接下來才執(zhí)行函數(shù)賦值,把一個(gè)
函數(shù)賦值給一個(gè)myFn變量,這時(shí)候如果調(diào)用myFn就可以了。