變數提升和函式提升的總結

  我們在學習JavaScript時,會遇到變數提升和函式提升的問題,為了理清這個問題,現做總結如下,希望對初學者能有所幫助

  我們都知道 var 宣告的變數有變數提升,而 let 宣告的變數和 const 宣告的常量沒有變數提升,但是為什麼 let 宣告的變數就不會變數提升呢?

首先我們得理解變數和函式的建立過程:

  變數宣告 ==>變數初始化 ==>變數賦值

  函式宣告 ==> 初始化 ==> 賦值

而變數提升和函式提升要完成的工作為:

  變數宣告 ==> 變數初識化(undefined)

  函式宣告 ==> 初始化 ==> 賦值(僅僅是賦值,並沒有執行函式體)

暫時性死區的概念:

  ES6規定,如果程式碼區塊中存在 let 和 const 命令宣告的變數,這個區塊對這些變數從一開始就形成了封閉作用域,直到宣告語句完成,這些變數才能被訪問(獲取或設定),否則就會報錯 ReferenceError。這在語法上稱為“暫時性死區”,即程式碼塊開始到變數宣告語句完成之間的區域。通過 var 宣告的變數擁有變數提升,沒有暫時性死區。

  因為有暫時性死區的存在,所以 let 宣告的變數和 const 宣告的常量才 不能 變數提升

  

  從變數建立過程來理解:

  let 宣告被提升了,但是並沒有做初始化工作

  const 宣告被提升,也是沒有做初始化工作(const只有宣告和初始化過程,沒有賦值過程)

    let 和 const 都沒有提升所要滿足的工作,所以就無法提升

  var 的宣告被提升,初始化被提升

  function 的宣告、初始化、和賦值都被提升

var 宣告的變數提升(宣告、初始化都被提升,但是賦值沒有提升)

  例項  

  console.log(a)  // undefined

  var a = 'hello world'

  console.log(a)  // 'hello world'

  實際執行過程如下

  var a

  console.log(a)  // undefined

  a = 'hello world'

  console.log(a)  // 'hello world'

function 宣告的函式提升(宣告、初始化、賦值都提升了)

  例項  

  fn()  // 'hello world'

  function fn() { console.log('hello world') }

  fn()  // 'hello world'

  實際執行過程如下

  function fn() { console.log('hello world') }  // 注意:此時函式體並沒有執行

  fn()  // 'hello world'

  fn()  // 'hello world'

既有 var 宣告的變數提升,又有 function 宣告的函式提升

  例項

  變數名和函式名不同名  

  console.log(a)  // undefined

  fn()

  var a = 100

  console.log(a)  // 100

  function fn() {

    console.log(a)  // undefined

    var a = 10

    console.log(a)  // 10

   }

  var a = 200

  console.log(a)  // 200

  實際執行過程如下 

  function fn() {

    console.log(a)  

    var a = 10

    console.log(a)   

   }

  var a  (全域性變數)

  var a  (區域性變數)

  console.log(a)  // undefined

  fn()  // undefined  10

  a = 100

  console.log(a)  // 100

  a = 200

  console.log(a)  // 200

  變數名和函式名同名 

  情況一:變數由 函式型別 變為 基本資料型別(如 Number)

  var a = 100

  console.log(a)  // 100

  a()  // 報錯(TypeError:a is not a function)

  function a() {

    console.log('hello world') 

   }

  實際執行過程如下

  function a() {

    console.log('hello world') 

   }

  var a  // 用 var 重新聲明瞭變數 a,但此時變數 a 指向的還是函式體本身,值未改變

  a = 100  //  重新給 a 賦值為 100,a 的值發生了改變,由函式型別變為了number型別

  console.log(a)  // 100

  a()  // 報錯:此時 a 已經不是函式 a 了

 

  情況二:變數重新賦值,但未立即改變變數的值

  console.log(a)  // a(){ console.log('hello world')}  即 a 函式本身

  a()  // 'hello world'

  var a = 100

  var a = 200

  var a = 300

  console.log(a)  // 300

  function a() {

    console.log('hello world') 

   }

  實際執行過程如下

  function a() {

    console.log('hello world')

   }

  var a  //  此時雖然重新聲明瞭 a 變數,但是並沒有改變 a 的值,此時的 a 還是個函式

  console.log(a)  // a(){ console.log('hello world')}  即 a 函式本身

  a = 100  // 重新給 a 變數賦值 100

  a = 200  // 重新給 a 變數賦值 200

  a = 300  // 重新給 a 變數賦值 300

  console.log(a)  // 100(此時變數 a 已經由 函式型別變為 基本資料型別number,且值為 100)

  a()  // 報錯:此時 a 已經不是函式 a 了

用 let 定義的變數和用 const 定義的常量 沒有提升

用 let 定義的變數

  在變數定義之前使用改變數 會報錯

  console.log(a)  // 報錯  Cannot access 'a' before initialization

  let a = 10

  

  在變數定義之後使用改變數 才能正常執行

  let a = 10  

  console.log(a)  // 10

總結:

  1. 用 var 定義的變數有變數提升,用 function 定義的函式有函式提升

  2. 函式提升優先順序高於變數提升,即優先執行函式提升

  3. let 定義的變數和 const 定義的常量不會提升

  4. 重複用 var 定義的同一個變數是合法的,如果重複的宣告有初始值,那就賦值;如果沒有初始值,那麼它不會對變數原來儲存的值不會產生影響

  5. 不能用 let 重複定義同一個變數,會造成錯誤