在一個JavaScript文件或一個JavaScript代碼塊的內部,瀏覽器會先對代碼進行預處理(編譯),而後再執行。瀏覽器
預處理會跳過執行語句,只處理聲明語句,一樣也是按從上到下按順序進行的。包括變量和函數在內的全部聲明都會在任何代碼被執行前首先被處理。 即便聲明是在調用的下方進行的,但瀏覽器仍然先聲明再調用(執行),這個現象叫作「提高」。因此,即使一個函數的聲明在下方,在前面仍然能夠正常執行這個函數。函數
注意1:對於聲明並賦值的語句,例如 var a = 1,在預處理階段會把這句話拆成兩句:blog
var a; a = 1;
也就是說,賦值或其餘邏輯運算是在執行階段進行的,在預處理階段會被忽略。ip
注意2:(1)函數聲明的提高優先於變量聲明的提高;(2)重複的var聲明會被忽略掉,可是重複的function聲明會覆蓋掉前面的聲明。內存
在預處理階段,聲明的變量的初始值是undefined, 採用function聲明的函數的初始內容就是函數體的內容。作用域
完成預處理以後,JavaScript代碼會從上到下按順序執行邏輯操做和函數的調用。io
首先咱們來看兩段簡短的代碼console
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,第二個是2function
按照C/C++第個段代碼輸出的結果因該是1和2,爲何第一個a無法輸出1呢?
分析緣由:
一、在js語言中,沒有相似於c語言這樣的塊級做用域。
二、js做用域鏈變量訪問規則:
(1)、當前做用域內存在要訪問的變量時,則使用當前做用域中的變量。
(2)、當前做用域中不存在要訪問的變量時,則會到上一層做用域中尋找,直到全局做用域。
三、執行順序:
(1)代碼的檢查裝載階段(預編譯階段),此階段進行變量和函數的聲明,可是不對變量進行賦值,變量的默認值爲undefined。
(2)代碼的執行階段,此階段對變量進行賦值和函數的聲明。
四、看上面的代碼:第一個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時,也會輸出後面定義的函數內容的緣由了。