JavaScript的變量提高有兩種,用var
聲明的變量以及用function
聲明的變量。javascript
咱們先來看下面這段代碼,a的值是多少java
代碼1編程
console.log(a); var a;
按照以往編程語言的思路來看,代碼自上而下運行,按這種思路,會報錯,由於執行到第2行時,變量a尚未定義,因此會報錯a is not defined
編程語言
然而事實上答案是undefined
函數
好,抱着疑惑,咱們看下面的代碼code
var a; console.log(a);
咱們發現,這兩段代碼是同樣的,那麼又有一個新的問題,是否是有沒有var a
都無所謂,它的答案始終是undefined
,才形成了覺得變量會提高的錯覺,因而我寫了代碼3ip
代碼3作用域
console.log(a);
好,它終於報錯了,因此這證實了javaScript
代碼並非自上而下執行的,至少從表面看上面是這樣的。開發
因而咱們再看代碼4編譯器
代碼4
console.log(a); var a = 2;
由於變量提高嘛,因此答案是2,然而事實上,它依然是undefined
,why?
這時候咱們有請編譯器這位負責語法分析及代碼生成等髒活累活的大佬。
編譯器在看到var a = 2;
,它會將其看作兩個聲明,var a;
和a = 2
,第一個聲明在編譯階段進行,第二個聲明會被原地等待執行階段。
也就是說上面代碼,會變成下面的這段代碼
var a; console.log(a); a = 2;
因此最終會是undefined
好,我在囉嗦一下,看這段代碼5
代碼5
a = 2; var a; console.log(a);
我想你們應該已經知道這段代碼執行時的真正順序及其答案了,沒錯,答案是2,但我想說的是把第2行給註釋掉,答案依然是2,但這個和變量提高沒啥關係了,是嚴格模式與非嚴格模式的鍋,在非嚴格模式下容許開發者能夠不使用聲明變量的關鍵字,但在嚴格模式下是不能夠的,它會報錯的。
與var
同樣,function
聲明的變量依然會提高。
log(5); function log(mes){ console.log(mes) }
按照以前的變量提高的理解,這段代碼的真正順序是這樣的,
function log(mes){ console.log(mes) } log(5);
很好,很正確,那麼再看下一段代碼
log(5); var log = function(mes){ console.log(mes) }
它報錯了,log is not a function
,從這裏咱們能夠看出,這種函數表達式是不會被提高的,只有函數聲明纔會被提高,試着在最前面新增一行代碼console.log(log)
,會先輸出undefined
。
因此這裏的真正順序是
var log; log(); //這時候只是聲明瞭log這個變量,並非函數,卻用函數的方法調用它,因此會報錯,說這不是一個函數。 log = function(mes){ console.log(mes) }
咱們雖然知道,var
聲明的變量會提高,但並不知道會提高到哪一個程度。
在此以前來看一段代碼
var a = 4; function foo(){ var a = 5; console.log(a); } foo(); console.log(a)
答案是5,4,先輸出5,再輸出4。
用var
聲明的變量是有函數做用域的,因此foo裏的a和foo外面的a沒有任何關係,這種狀況正是我想要的。
再改下代碼
function foo(){ a = 5 console.log(a); var a; } foo(); console.log(a)
答案是5,a is not defined
第4行代碼輸出5,第9行報錯。
這種狀況就是變量提高只會提高到變量所在的 做用域的頂部,不會提高到父級做用域。
所以能夠得出一個結論:變量提高只會將變量提高到本身所在的做用域的頂部
既然用var
和function
的變量都有提高的功能,那若是同一個變量用這兩種都聲明會怎樣,好吧,看標題就知道了,函數優先。
具體看下代碼
foo(); var foo; function foo(){ console.log(1) } foo = function(){ console.log(2) }
答案是1
這段代碼其實這樣子的
function foo(){ console.log(1) } foo();// 1 foo = function(){ console.log(2) }
仔細一看,var foo;
沒了,沒錯,它被引擎忽略了,認爲重複聲明因此把它拋棄了。
好,既然var
聲明的變量比不了函數聲明,那就用函數聲明,屢次聲明同個變量。
foo() function foo(){ console.log(1); } foo() function foo(){ console.log(2); } foo() function foo(){ console.log(3); } foo()
foo
聲明瞭三次,調用了四次,每次調用的結果都是3,因此最後的函數聲明會覆蓋以前的函數聲明
可是var
還想掙扎一下,以爲仍是有必要證實本身的存在感的。
foo() function foo(){ console.log(1); } var foo; foo() foo = function(){ console.log(2); } foo() function foo(){ console.log(3); } foo()
仔細看,中間那部分代碼改了,依次輸出3,3,2,2
雖然var foo
被忽略了,但下面的函數仍是有用的,這段代碼能夠當作是這樣的
function foo(){ console.log(3); } foo();//3 foo();//3 foo = function(){ console.log(2); } foo();//2 foo();//2
以前是在做用域聲明函數,如今來塊裏面聲明函數
function foo(){ console.log(b); // undefined b(); //TypeError: b is not a function var a = true; if(a){ function b(){ console.log(2) } //下面這段代碼和上面的結果同樣 // var b = function(){ // console.log(2) // } } //b() --> 這裏會被執行 } foo()
從上面看上去,b是undefined
,證實這個變量仍是有的,只不過它並非一個函數,這狀況和用函數表達式差很少。
var
,聲明函數用function