深入瞭解JavaScript中let/var/function的變量提升

前言

在我們的印象中,當提到JavaScript中的變量提升,我們想到的是“變量和函數的聲明在物理層面移動到代碼的最前面“。當然這麼說不大準確,變量和函數聲明在代碼裡的位置是不會變的,而是在編譯階段被放入內存中。

可是如果我問你:let 到底有沒有提升?如果有,var / let / function三者的變量提升一致嗎?此時你的答案是什麼?

接下來讓我們來探討這兩個問題,如有錯誤敬請指正。

1. let存在提升

對於let是否存在提升這個問題,讓我們先來看以下這段代碼:

a = "global";
(function() {
    console.log(a); // undefined, 而不是打印出global
    var a;
}());

{
    console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
    let a = 1;
}

對於在函數作用域下打印出undefined,我們應該不奇怪。但引起我註意的是在塊作用域下,拋出引用錯誤的原因是在初始化之前找不到a。那有沒有可能a創建過程被提升?而在我發現ES文檔中存在[var/let hoisting],這讓我有理由猜測let存在提升,隻是由於暫時性死區的原因,我們不能在let a之前使用 a

為瞭證明我的猜想,我想先從let聲明的創建、初始化和賦值過程入手。

{
  let a = 0;
  a = 1;
}

上述的代碼塊中發生的過程如下:

  • 找到所有用let聲明的變量,在環境創建變量;
  • 執行代碼;
  • 執行a=0,將a初始化為1;
  • 執行a=1,對a進行賦值。

由該過程可知,如果我們在創建完變量後和初始化之前執行log()方法,控制臺將會報錯:

let a = 'global'
{
  console.log(a) // Uncaught ReferenceError: Cannot access 'a' before initialization
  let a = 1
}

由此一來我們就可以知道瞭let在創建的過程被提升,而在初始化過程沒有被提升。

2. var/function的變量提升

接下來我們來看看function/var的創建、初始化和賦值過程,由此看看能否探究出它們的差別。

2.1 var的變量提升

function foo() {
  console.log(a);  // undefined
  var a = 0;
  var b = 1;
}
foo();

當我們執行foo()時,發生以下過程(較為省略):

  • foo創建環境;
  • 找到foo中所有被var聲明的變量,在這個環境中創建變量;
  • 將變量初始化為 undefined;
  • 執行代碼;
  • a賦值為0,b賦值為1。

由此我們可以知道var聲明在代碼執行前創建變量並初始化,所以當我們在var a = 0之前執行log(a)方法會得到 undefined 的結果。

2.2 function的變量提升

foo('btqf');

function foo(name) {
  console.log("my name is" + name)  // my name is btqf
}

當我們執行foo函數時,發生以下過程:

  • 找到所有function聲明的變量,在環境中創建變量;
  • 將這些變量初始化並賦值;
  • 執行代碼。

由此可見,function聲明在代碼執行前就創建、初始化並賦值。

3. 總結

  • let 的「創建」過程被提升瞭,但是初始化沒有提升。
  • var 的「創建」和「初始化」都被提升瞭。
  • function 的「創建」「初始化」和「賦值」都被提升瞭。
  • 函數和變量相比,會被優先提升,即函數會被提升到更靠前的位置。

值得一提的是constlet基本類似,唯一的區別在於const隻有創建和初始化,沒有賦值過程。

到此這篇關於深入瞭解JavaScript中let/var/function的變量提升的文章就介紹到這瞭,更多相關let/var/function變量提升內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: