有些朋友可能會以爲javascript的代碼是從上到下,一行一行的解釋執行的。若是按照這樣的思路,在有些狀況下閱讀代碼會獲得錯誤的結果,考慮如下代碼:javascript
a = 2; var a; console.log(a);
console.log(a)
應該輸出什麼呢?有些開發者以爲會輸出undefined
,由於var a
在'a = 2'以後,變量a
被重複定義了,可是沒有被賦值,因此是'undefined'。可是結果輸出是2
。以下圖所示:java
咱們再來考慮另外一段代碼,以下所示:函數
console.log(a); var a = 2;
這段代碼會輸出什麼樣的結果呢?有些人可能會以爲輸出ReferenceError
。由於變量a
在沒有聲明的狀況下就被使用了。真實結果呢,以下圖所示:輸出的是undefined
優化
爲何會這樣呢?這就牽出了本文的主題:JavaScript聲明提高spa
在JavaScript代碼運行以前實際上是有一個編譯階段的。編譯以後纔是從上到下,一行一行解釋執行。變量提高
就發生在編譯階段,它把變量和函數的聲明提高至做用域的頂端。(編譯階段的工做之一就是將變量與其做用域進行關聯)。
因此對於代碼var a =2;
來講,編譯器看到的是兩行代碼var a; a = 2;
第一個語句是聲明語句,在編譯階段處理。第二個語句是賦值語句,在運行階段處理。
那麼咱們再回過頭來看看問題中出現的代碼:3d
a = 2; var a; console.log(a);
應該這樣來處理:code
var a; //編譯階段 a = 2; //運行階段 console.log(a); //運行階段
因此這段代碼的最終輸出的結果是2
。 blog
第二段代碼:圖片
console.log(a); var a = 2;
應該這樣來處理:ip
var a; //編譯階段 console.log(a); //運行階段 a = 2; //運行階段
因此這段代碼的最終輸出結果是undefined
。
變量提高
須要注意兩點:
提高的部分只是變量聲明,賦值語句和可執行的代碼邏輯還保持在原地不動
提高只是將變量聲明提高到變量所在的變量範圍的頂端,並非提高到全局範圍,說明以下:
foo(); function foo(){ console.log(a); //會輸出undefined var a = "2"; } //變量提高以後的效果 function foo(){ var a; console.log(a); a = "2"; } foo();
函數聲明會提高,可是函數表達式就不了
。看以下代碼:
foo(); var foo = function bar(){ //這是一個函數表達式,再也不是函數聲明。 console.log("bar"); }
處理方式以下:
var foo; foo(); //TypeError,由於尚未賦值 bar(); //bar不能夠在全局範圍內引用 foo = function bar(){ console.log("bar"); }
變量聲明和函數聲明都會獲得變量提高,但函數聲明會最早獲得提高,而後是變量聲明。
考慮以下代碼:
foo(); //輸出的結果爲1 var foo; function foo(){ console.log(1); } foo = function(){ console.log(2); }
處理方式以下:
function foo(){ console.log(1); } foo(); foo = function(){ console.log(2); }
注意:var foo;
因爲是重複聲明變量,因此被編譯優化去掉。
對於函數聲明來講,若是定義了相同的函數變量聲明,後定義的聲明會覆蓋掉先前的聲明,看以下代碼:
foo(); //輸出3 function foo(){ console.log(1); } var foo = function(){ console.log(2); } function foo(){ console.log(3); }
JavaScript中是沒有塊級做用域的概念(ps:ES6中有改進了),看以下代碼:
foo(); //輸出結果爲2 var a = true; if(a){ function foo(){ console.log(1); } }else{ function foo(){ console.log(2); } }
這段代碼輸出結果爲2,if語句沒有塊級做用域的功能,因此函數聲明都被提高到全局做用域中,又由於定義了兩個foo,後來的定義覆蓋了前邊的定義,因此輸出結果爲2。