這篇部落客要講解JavaScript的執行順序,通過這篇部落格可以理解為什麼先使用再聲明有時候可以有時候卻不可以、JavaScript代碼在各種情況下的執行順序等問題。
文檔流
HTML文檔再瀏覽器中的解析順序,是按照文檔流從上到下逐步解析頁面結構和資訊。JavaScript作為嵌入指令碼也是HTML文檔的組成部分,所以JavaScript代碼在裝載時也是根據指令碼標籤<script>的順序確定的。
介面指令碼
例如下面的代碼:
<script type="text/javascript">alert('我是最前面的指令碼')</script><html><head><script type="text/javascript">alert('我是頭部指令碼')</script></head><body><script type="text/javascript">alert('我是頁面指令碼')</script></body></html><script type="text/javascript">alert('我是最後面的指令碼')</script>
順序為:最前面---頭部---頁面---最後面,依次彈出。
引用指令碼
引用的外部指令碼也將按照引用出現的順序執行,不會因為是外部指令碼而延期執行,例如把頭部指令碼和頁面指令碼作為引用檔案時:
<script type="text/javascript">alert('我是最前面的指令碼')</script><html><head><script src="head.js" type="text/javascript"></script></head><body><script src="body.js" type="text/javascript"></script></body></html><script type="text/javascript">alert('我是最後面的指令碼')</script>
出現的順序仍為:最前面---頭部---頁面---最後面。
與先行編譯
上一篇部落格提到,JavaScript解析指令碼時,在編譯期即對所有聲明的變數和函數進行處理。
變數
所以一下指令碼在執行時不會出錯:
<html><head><script type="text/javascript">alert(a);var a=1;alert(a);</script></head><body></body></html>
執行結果為:
這是因為在先行編譯期,已經對所有變數進行了處理,第一次彈出時,解譯器已經知道了a的存在,所以並沒有提示錯誤。你一定想問,既然知道a的存在,為什麼第一次彈出的是undefined?這是因為變數在執行期才會對變數賦值,變數的聲明和賦值處在不同的階段;第二次彈出的是“1”,這是因為在第二次彈出時,已經對a賦值,故提示為1。
需要注意的是,顯示undefined並不是錯誤,而是遍曆範圍鏈沒有找到值,如果把a換成b,才是錯誤,提示如下:
函數
同理,函數在聲明前調用也是合理的例如:
<html><head><script type="text/javascript">f();function f(){alert('I am a function');}</script></head><body></body></html>
運行結果:
但是以下這樣會提示法錯誤:
這兩次代碼如此相似,為什麼確實完全不同的結果?這是因為第二次,我們把f當做一個變數處理,所以在先行編譯期,JavaScript解譯器只能夠為變數f進行處理,對於f的值,只能等到運行期才能賦值,所以提示找不到對象f。
按塊執行
此處所謂的塊指的是<script>標籤分隔的代碼塊,JavaScript解譯器在執行指令碼時,是按照塊來執行的,也就是說:瀏覽器在解析HTML文檔流時,如果遇到<script>標籤,則解譯器會等到把這個塊載入完後,先對塊進行先行編譯,然後再執行。
正因為如此,當前面一個塊調用後面一個塊中聲明的變數或函數時會提示法錯誤:
<html><head><script type="text/javascript">f();</script><script type="text/javascript">function f(){alert('I am a function');}</script></head><body></body></html>
錯誤提示:
與此同在的是,JavaScript雖然按塊執行,但是不同的塊都屬於同一個全域範圍,也就是說,塊之間的變數和函數可以共用。
事件機制
如上面所說,JavaScript按塊執行,同時又遵循HTML文檔流的解析順序,所以上面的代碼會提示錯誤。但是如果文檔載入完畢再運行就不會出錯,所以我們可以用事件來解決:
<html><head><script type="text/javascript">window.onload=function(){f();};</script><script type="text/javascript">function f(){alert('I am a function');}</script></head><body></body></html>
運行結果:
輸出指令碼 輸出指令碼指的是動態添加的指令碼,例如:
<html><head></head><body><script type="text/javascript">document.write("<script type='text/javascript'>");document.write(' alert(1); ');document.write("<\/script>")</script> </body></html>
瀏覽器會彈出框“1”。 輸出指令碼的處理過程是:document.write()方法先把輸出的字串寫入到指令碼所在文檔,瀏覽器在解析完畢document.write()文檔後,繼續解析document.write輸出的內容,然後再解析後面的HTML文檔,也就是說,JavaScript的代碼字串,會在輸出後馬上執行。 需要注意的是,輸出的如果是JavaScript指令碼,一定要放在:
<script type="text/javascript">document.write("<script type='text/javascript'>");//要輸入的指令碼document.write("<\/script>")</script>
之間,否則會當做一般字元出現,而不會執行(Chrome瀏覽器)。 另外因為不同瀏覽器對document.write()輸出的運行指令碼和用document.write()引用的外部執行順序不同,可能出現解析錯誤,所以可以二者放在不同的<script>標籤下,相容瀏覽器。總結
上一篇部落格我們介紹了JavaScript解析機制,比較抽象,而這篇部落格說的執行順序就比較具體,我們可以直觀感覺到這種執行順序。