淺析JavaScript中的提高

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中的變量聲明

ES6新增了let、const聲明,它們會存在一個暫時性死區(TDZ),表現出的狀況會有所不一樣,具體能夠參考:let, const

總結

  • var變量聲明和函數聲明存在提高
  • 函數表達式不會被提高
  • 函數聲明提高的優先權大於普通變量聲明
  • let, const因爲存在暫時性死區,表現出的狀況和var聲明有所不一樣
相關文章
相關標籤/搜索