廢話不多說,直奔主題了。 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程式碼
所謂程式碼區塊就是使用<script>標籤分隔的程式碼段。 JavaScript解釋器在執行腳本時,是按區塊來執行的。通俗地說,就是瀏覽器在解析HTML文件流程時,如果遇到一個<script>標籤,則JavaScript解釋器會等到這個程式碼區塊都載入完後,先對程式碼區塊進行預編譯,然後再執行。執行完畢後,瀏覽器會繼續解析下面的HTML文件流,同時JavaScript解釋器也準備好處理下一個程式碼區塊。由於JavaScript是按區塊執行的,所以如果在一個JavaScript區塊中呼叫後面區塊中宣告的變數或函數就會提示語法錯誤。 </script>
<script> alert(a); </script> <script> var a = 1; </script>
上面的这段代码,由于是两个代码块,先执行完第一个代码块,再执行第二个代码块。执行第一个代码块的时候,变量a没有声明,所以报错,报错信息是:a is not defined。
<script> var a = 1; </script> <script> alert(a); </script>
虽然说,JavaScript是按块执行的,但是不同块都属于同一个全局作用域,也就是说,块之间的变量和函数是可以共享的。所以,上面的这两个代码块运行的时候,虽然是两个代码块,但是第一段运行以后,a变量就存在了全局作用域中,此时运行到第二个代码块,输出的a变量就可以调用全局作用域中的a,所以没有问题。
4、借助事件机制改变javascript执行顺序
由于JavaScript是按块处理代码,同时又遵循HTML文档流的解析顺序,所以在上面示例中会看到这样的语法错误。但是当文档流加载完毕,如果再次访问就不会出现这样的错误。为了安全起见,我们一般在页面初始化完毕之后才允许JavaScript代码执行,这样可以避免网速对JavaScript执行的影响,同时也避开了HTML文档流对于JavaScript执行的限制。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>javascript</title> <script> window.onload = function() { alert(a); }; </script> <script> var a = 1; alert("bb"); </script> </head> <body> </body> <script> alert("cc"); </script> </html>
windows.onload = function()表示先在触发事件上加一个函数,并不立即执行,而是在整个页面都加载完成以后再开始执行该事件,及function。所以,在windows.onload执行之前,就已经把一些变量加载到了全局区中,所以没有问题。上面的输出结果是:先输出bb,再输出cc,最后输出a。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>javascript</title> <script> window.onload = function() { alert(a); }; // 上面的onload不会执行,只会执行下面的onload window.onload = function() { alert("onload2"); }; </script> <script> var a = 1; alert("bb"); </script> </head> <body> </body> <script> alert("cc"); </script> </html>
如果在一个页面中存在多个windows.onload事件处理函数,则只有最后一个才是有效的(如上面的代码所示),为了解决这个问题,可以把所有脚本或调用函数都放在同一个onload事件处理函数中,如下面的代码所示:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>javascript</title> <script> window.onload = function() { // 放到一起 alert(a); alert("onload2"); }; </script> <script> var a = 1; alert("bb"); </script> </head> <body> </body> <script> alert("cc"); </script> </html>
5、javascript输出脚本的执行顺序
在JavaScript开发中,经常会使用document对象的write()方法输出JavaScript脚本。document.write()方法先把输出的脚本字符串写入到脚本所在的文档位置,浏览器在解析完document.write()所在文档内容后,继续解析document.write()输出的内容,然后才按顺序解析后面的HTML文档。也就是说,JavaScript脚本输出的代码字符串会在输出后马上被执行。请注意,使用document.write()方法输出的JavaScript脚本字符串必须放在同时被输出的3f1c4e4b6b16bbbd69b2ee476dc4f83a标签中,否则JavaScript解释器因为不能够识别这些合法的JavaScript代码,而作为普通的字符串显示在页面文档中。但是,通过document.write()方法输出脚本并执行也存在一定的风险,因为不同JavaScript引擎对其执行顺序不同,同时不同浏览器在解析时也会出现Bug。
以上所述是小编给大家介绍的JavaScript语句的执行过程,希望对大家有所帮助。