廢話不多說,直奔主題了。 javascript的運作原理總結如下:
1、依照html文檔流程順序執行javascript程式碼
瀏覽器是按照文檔流從上到下逐步解析頁面結構和資訊的,javascript程式碼作為嵌入的腳本作為html文檔的組成部分,所以javascript程式碼在加載時的執行順序也是根據腳本標籤<script>的出現順序來決定的。 </script>
如果透過腳本標籤<script>的src屬性來引入外部.js文件,那麼它也將按照其語句出現的順序來執行,而且執行過程是文件載入的一部分。不會因為是外部js檔而延期執行。 </script>
2、預先編譯和執行順序的關係
首先看如下這段程式碼:
<script type="text/javascript"> function hello() { alert("hello"); } hello(); function hello() { alert("hello world"); } hello(); </script>
上面這段js程式碼的輸出結果是hello world 、hello world,而不是先輸出hello,再輸出hello world。這是因為javascript並非完全按照順序來解釋執行,而是在解釋之前會對javascript進行一次預編譯,在預編譯的過程中,會把定義式的函數優先執行,也會把所有var變數創建,預設值為undefined,以提高程式的執行效率。也就是說上面的這段程式碼其實是被JS引擎預先編譯成下面這樣:
<script type="text/javascript"> var hello = function() { alert("hello"); }; hello = function() { alert("hello world"); }; hello(); hello(); </script>
透過上面的程式碼可以清楚的看到,函數其實也是變量,可以對函數進行賦值。為了防止前面那種情況的出現,可以如下定義成兩個js檔案:
<script type="text/javascript"> hello(); function hello() { alert("hello"); } // hello(); </script> <script type="text/javascript"> function hello() { alert("hello world"); } hello(); </script>
上面第一個文件,我把hello()放在了function的前面,也是可以輸出正確結果的。
<script type="text/javascript"> hello(); var hello = function() { alert("hello"); }; // hello(); </script>
如果用上面的這種方法對function函數進行定義,那麼就會報錯,報錯資訊如下圖1所示:
這裡報錯hello is not a funtion,這是由於在預編譯的時候,對於用var聲明的變量,雖然最先就處理了,但是變量值是undefined。然後執行hello()的時候,由於前面的hello是undefined,類型沒有確定,所以這裡是hello is not a function。雖然,程式中有定義這個函數,但是定義的位置放在了呼叫的後面,那麼呼叫的時候,程式並沒有運行到這裡,所以沒用。
再來看下面的這一段程式碼:
<script type="text/javascript"> hello(); function hello() { alert("hello"); } // hello(); </script>
上面的這段程式碼雖然呼叫也是在函數定義的前面,但是這裡是以function關鍵字來定義的,用function來定義的時候,跟var不一樣,function定義的時候就已經把函數的值賦了過去,所以這裡可以運行。
總結:
當javascript引擎解析腳本時,它會在預編譯期對所有宣告的變數和函數進行處理。處理如下:
(1)在執行前會進行類似「預先編譯」的操作:首先會建立一個目前執行環境下的活動對象,並將那些以var宣告的變數設為活動對象的屬性,但此時這些變數的賦值都是undefined,並將那些以function定義的函數也加入為活動物件的屬性,而且它們的值正是函數的定義。
(2)在解釋執行階段,遇到變數需要解析時,會先從目前執行環境的活動物件中查找,如果沒有找到且執行環境的擁有者有prototype屬性時則會從prototype鏈中查找,否則將會依照作用域鏈查找。遇到var a = ...這樣的語句時會給對應的變數賦值(注意:變數的賦值是在解釋執行階段完成的,如果在這之前使用變量,它的值會是undefined)。
(3)綜上,一句話總結就是:變數的宣告在預編譯期,變數的初始化在運行期。
<script type="text/javascript"> alert(a); // 在预编译期间a变量已经加载,但是用var定义,所以赋值为undefined先,故这里输出undefined。 var a = 1; // 这里给前面的没有赋值的a进行赋值为1 alert(a); // 这里输出的a已经是前面赋值过的,所以输出1。 </script>
對於上面的這段程式碼,輸出結果是:先輸出undefined,後輸出1,分析見程式碼備註。
雖然變數和函數宣告可以在文件任意位置,但是良好的習慣應該是在所有JavaScript程式碼之前宣告全域變數和函數,並對變數進行初始化賦值。在函數內部也是先宣告變量,然後再引用。
3、以區塊執行javascript程式碼
所謂程式碼區塊就是使用