一、先預處理后執行
在一個JavaScript文件或一個JavaScript代碼塊的內部,瀏覽器會先對代碼進行預處理(編譯),然后再執行。
預處理會跳過執行語句,只處理聲明語句,同樣也是按從上到下按順序進行的。包括變量和函數在內的所有聲明都會在任何代碼被執行前首先被處理。 即使聲明是在調用的下方進行的,但瀏覽器仍然先聲明再調用(執行),這個現象叫做“提升”。所以,即便一個函數的聲明在下方,在前面仍然可以正常執行這個函數。
注意1:對於聲明並賦值的語句,例如 var a = 1,在預處理階段會把這句話拆成兩句:
var a; a = 1;
也就是說,賦值或其他邏輯運算是在執行階段進行的,在預處理階段會被忽略。
注意2:(1)函數聲明的提升優先於變量聲明的提升;(2)重復的var聲明會被忽略掉,但是重復的function聲明會覆蓋掉前面的聲明。
在預處理階段,聲明的變量的初始值是undefined, 采用function聲明的函數的初始內容就是函數體的內容。
二. 執行順序
完成預處理之后,JavaScript代碼會從上到下按順序執行邏輯操作和函數的調用。
實例一:
首先我們來看兩段簡短的代碼
var a=1; function func(){ console.log(a); //var a=2; console.log(a); } func();
運行結果為:在這里打印出來的a都是1
var a=1; function func(){ console.log(a); var a=2; console.log(a); } func();
運行結果:在這里第一個a打印出來的結果是undefined,第二個是2
按照C/C++第個段代碼輸出的結果因該是1和2,為什么第一個a沒法輸出1呢?
分析原因:
1、在js語言中,沒有類似於c語言這樣的塊級作用域。
2、js作用域鏈變量訪問規則:
(1)、當前作用域內存在要訪問的變量時,則使用當前作用域中的變量。
(2)、當前作用域中不存在要訪問的變量時,則會到上一層作用域中尋找,直到全局作用域。
3、執行順序:
(1)代碼的檢查裝載階段(預編譯階段),此階段進行變量和函數的聲明,但是不對變量進行賦值,變量的默認值為undefined。
(2)代碼的執行階段,此階段對變量進行賦值和函數的聲明。
4、看上面的代碼:第一個a輸出undefined。原因:js作用域鏈的訪問規則,當前作用域內存在要訪問的變量a,所以使用當前作用域中的變量。再根據js代碼的執行順序,此時的a只是聲明了而並未被賦值,默認為undefined,所以輸出undefined。而第二個a,輸出1,正是因為此時的a已經被聲明且被賦值,所以a輸出1。
在當前作用域內存在要訪問的變量a,則就會使用當前作用域的變量a,只要當前作用域存在該變量即是對該變量進行了聲明(即不會再用作用域外的值),直到var a=2;才是對該變量進行賦值。在代碼中先是執行了console.log(a);在執行var a=2;所以此時a在該作用域內只是進行了聲明還未進行賦值,所以就會輸出undefined.
實例二:
例子1
var hello = function(){ console.log('hello,zhangsan'); } hello(); var hello = function(){ console.log('hello,lisi'); } hello();
運行結果:
例子2
function hello(){ console.log('hello,zhangsan'); } hello(); function hello(){ console.log('hello,lisi'); } hello();
運行結果:
JavaScript執行引擎並非一行一行地分析和執行程序,而是一段一段地分析執行的。而且在分析執行同一段代碼中,定義式的函數語句會被提取出來優先執行。函數定義執行完后,才會按順序執行其他代碼。
問題:在例子2中,兩次調用都會輸出相同的內容“hello,lisi”。同樣是聲明兩個相同名稱的函數,為什么調用的結果卻不一樣呢?
這就是JavaScript執行順序導致的。JavaScript執行引擎並非一行一行地分析和執行程序,而是一段一段地分析執行的。而且在分析執行同一段 代碼中,定義式的函數語句會被提取出來優先執行。函數定義執行完后,才會按順序執行其他代碼。也就是說,在第一次調用hello函數之前,第一個函數語句 定義的代碼已經被第二個函數定義語句的代碼覆蓋了,這就是為什么在例子2中第一次調用hallo時,也會輸出后面定義的函數內容的原因了。