JavaScript代碼在執行時並不徹底是由上到下一行一行執行的,由此產生了一個提高的問題。javascript
能夠簡單理解爲:聲明(變量和函數)都會被「移動」到各自做用域的最頂端,這個過程被稱爲提高。java
下面兩個例子a會log什麼出來呢?git
a = 233; var a; console.log(a)
console.log(a); var a = 233;
「看起來」第一個例子應該log出undefined,第二個例子變量a在使用前沒有先進行聲明,所以會拋出ReferenceError異常。但實際上,第一個例子a輸出233,第二個例子a輸出undefined。 es6
爲何? github
在知道爲何以前有必要了解:JavaScript雖然是解釋型語言,但在解釋JavaScript代碼以前首先對其進行編譯的。編譯階段中的一部分工做就是找到全部的聲明,並用合適的做用域將它們關聯起來。函數
變量和函數在內的全部聲明都會在任何代碼被執行前首先被處理。code
var a = 233;
上面的聲明看起來是一個聲明,而對於JavaScript而言實際上這是兩個聲明:var a;和a = 233;。第一個定義聲明是在編譯階段進行的。第二個賦值聲明會被留在原地等待執行階段。 ip
看回前面兩個例子,第一個例子的代碼會被這樣處理:作用域
var a; a = 233; console.log(a);
第二個例子:get
var a; console.log(a); a = 233;
注意:只有聲明自己會被提高,而賦值或其餘運行邏輯會留在原地 。
第一個例子:
foo(); function foo() { console.log(a); // undefined var a = 233; }
函數foo能夠正常執行。
第二個例子:
foo(); // TypeError var foo = function bar() { // ... }
報錯:Uncaught TypeError: foo is not a function
由此能夠看到:函數聲明會被提高,可是函數表達式卻不會被提高。
第三個例子:
foo(); // TypeError bar(); // ReferenceError var foo = function bar() { // ... };
這段代碼通過提高後,能夠理解爲如下形式:
var foo; foo(); // TypeError bar(); // ReferenceError foo = function() { var bar = ...self... // ... };
因而可知:具名的函數表達式,名稱標識符在賦值以前也沒法在所在做用域中使用。
既然函數聲明和變量聲明都會被提高,那麼問題來了,重複聲明的代碼中的優先級是怎樣的,是函數聲明被提高仍是變量聲明被提高?
繼續驗證:
foo(); // 1 var foo; function foo() { console.log( 1 ); } foo = function() { console.log( 2 ); }
var foo儘管出如今 function foo()...的聲明以前,但它是重複的聲明而被忽略,由於函數聲明會被提高到普通變量以前。能夠得知:函數聲明提高的優先權大於普通變量聲明。
儘管重複的var聲明會被忽略掉,但出如今後面的函數聲明仍是能夠覆蓋前面的:
foo(); // 3 function foo() { console.log( 1 ); } var foo = function() { console.log( 2 ); }; function foo() { console.log( 3 ); }
ES6新增了let、const聲明,它們會存在一個暫時性死區(TDZ),表現出的狀況會有所不一樣,具體能夠參考:let, const