函數聲明與函數表達式、變量提高

函數的聲明方式

在定義一個函數的時候一般有兩種聲明方式:javascript

foo(){};     // 函數聲明
var foo = function(){};    // 函數表達式

不一樣之處

  1. 函數表達式後面加括號能夠直接執行
  2. 函數聲明會提早預解析

預解析

讓咱們先看一個例子:css

foo();         //  函數聲明
foo_later();     //  foo_later is not a function

function foo(){ console.log('函數聲明'); }
var foo_later = function(){ console.log('函數表達式'); }

能夠看到,函數聲明foo被預解析了,它能夠在其自身代碼以前執行;而函數表達式foo_later則不能。要解決這個問題,咱們先要弄清楚JavaScript解析器的工做機制。java

變量提高(hoist)

JavaScript解析器會在自身做用域內將變量和函數聲明提早(hoist),也就是說,上面的例子其實被解析器理解解析成了如下形式:express

function foo(){ console.log('函數聲明'); }    // 函數聲明所有被提早
var foo_later;     // 函數表達式(變量聲明)僅將變量提早,賦值操做沒有被提早

foo();             
foo_later();     


foo_later = function(){ console.log('函數表達式'); }

這樣也就能夠解釋,爲何在函數表達式以前調用函數,會返回錯誤了,由於它尚未被賦值,只是一個未定義變量,固然沒法被執行。函數

一樣的,咱們也能夠試着猜想下面這段代碼的輸出結果:spa

console.log(declaredLater);   

var declaredLater = "Now it's defined!";

console.log(declaredLater);    

該段代碼能夠被解析成一下形式:code

var declaredLater;          

console.log(declaredLater);    // undefined

declaredLater = "Now it's defined!";

console.log(declaredLater);    // Now it's defined!

變量聲明被提到最前(因此不會報出變量不存在的錯誤),但賦值沒有被提早,因此第一次的輸出結果是undefined。blog

須要注意的是

因爲函數聲明會被預解析,因此不要使用此種方法來聲明不一樣函數。嘗試猜測下面例子的輸出結果:ip

if(true){
  function aaa(){
    alert('1');
  }  
}
else{
  function aaa(){
    alert('2');
  }
}

aaa();

與咱們預想的不一樣,該段代碼彈出的是「2」.這是由於兩個函數聲明在if語句被執行以前就被預解析了,因此if語句根本沒有用,調用aaa()的時候直接執行了下面的函數。作用域

總結

經過上面的講解能夠總結以下:

  • 變量的聲明被提早到做用域頂部,賦值保留在原地
  • 函數聲明整個「被提早」
  • 函數做爲值賦給變量時只有變量「被提早」了,函數沒有「被提早」

經過練習上面的實例本身多感覺一下。另外,做爲最佳實踐:變量聲明必定要放在做用域/函數的最上方(JavaScript 只有函數做用域!)。

 

參考:

JavaScript 中對變量和函數聲明的「提早(hoist)」

函數聲明 VS 函數表達式

相關文章
相關標籤/搜索