清掃變量聲明提高

前言

js 中存在變量提高,前端er基本都知道,可是這個變量提高的是什麼,什麼階段提高的,var,let和function等關鍵字聲明的變量在if條件語句中是否有提高,提高是否能穿透條件語句的執行體?不知道有多少人對這些有深刻理解。javascript

先上代碼:前端

var b = true;
if(b) {
    function a() {
        console.log('a');
    }
} else {
    function a() {
        console.log('b');
    }
}
a();
複製代碼

當你讀完這段代碼後,若是心中沒有準確且肯定的知道它是怎麼運行的,那麼你本文可能對你有所幫助。java

js 的預處理

JS 執行前,會對代碼進行預處理,預處理過程會提早處理var、function聲明、class、const、let等關鍵字聲明的變量。函數

聲明提高規則

在變量的聲明提高中存在一些日常咱們可能不太注意的細節,這裏將聲明提高的具體行爲分紅三類進行總結,分別是:ui

  • var 聲明
  • function 聲明
  • class、let 和 const聲明

var 聲明

var 聲明的變量提高時,只管在當前做用域內聲明這個變量並初始化其值爲undefined。來看一個實際的例子:編碼

function s() {
    console.log(a);
    var a = 'ss';
}

s();
複製代碼

這段代碼打印的結果是undefined,因爲 var a在預處理時被提高至函數s的做用域最開始,並初始化爲 undefined,因此打印結果是undefined。spa

再來看另外一段code

function test() {
    console.log(a);
    if(false) {
        var a = 'ss';
    }
}
test();
複製代碼

這段代碼的打印的結果也是 undefined,雖然判斷條件爲 false 的語句永遠不會被執行,可是 var 的聲明提高是無論這些的,它的提高能夠穿透條件語句直達當前做用域的頂部,至於執不執行聲明的提高是無論的。cdn

總之 var 的提高就一句,提高到當前做用域的頂部,並初始化其值爲undefined。blog

function 聲明

相較於 var 的提高簡單明瞭,function 的提高則要複雜一些,考慮下面這段代碼:

function test() {
    console.log(a);
    function a() {
        
    }
}
test();
複製代碼

這段代碼比較簡單,會打印函數出一個函數a,而不是undefined,也就是說函數聲明不只僅是變量聲明的提高,同時給變量 賦值了。那麼是否是不管什麼時候,函數聲明的提高都是及提高變量聲明還賦值呢?考慮下面這段代碼:

function test() {
    console.log('函數a', a);
    if(false) {
        function a() {
            
        }
    }
}

test();
複製代碼

這段代碼的打印結果是undefined,若是沒有函數聲明,直接打印a,這裏會拋出 not defined 的錯誤。

這說明在 if 語句中 function 的聲明名仍然會提高,只是被賦值爲 undefined 了,其具體的賦值發生在了代碼執行階段。

class、let 和 const聲明

class、let 和 const的聲明提高具備相同特徵,因此這裏我只說如下 let。

先考慮下面這段的代碼:

function test() {
    console.log(a);
    let a = 'aLet';
}
test();
複製代碼

嗯,這段代碼會拋錯,就像這樣

如今去網上搜一下關於 let 的變量提高的內容,仍然會看到不少說 let 不存在變量聲明提高的說法,給出的理由是,既然提高了,爲何在聲明以前使用該變量會報錯呢?下面就說一說爲何認爲 let 是存在提高的。 考慮下面代碼:

function test() {
    console.log(c)
    var c = 'tVar';
    let c = 'cLet';
};
test();
複製代碼

這段代碼會拋處錯誤,告訴你已經存在同名變量了,不能再定義c,按道理來講正常結果應該會打印 undefined 纔對,畢竟var c 聲明的 c 會提高並被賦值爲 undefined 。若是去掉let c的定義,則會按咱們預期的打印 undefined ,也就是說出如今後面的 let 聲明影響了前面語句的結果。

這說明 let 聲明的變量在預處理階段仍然會被處理,只不過這種處理只是單純的在聲明做用域中提高了變量的聲明,而沒有給變量初始化,而在變量被初始化以前是沒法使用的,也就出如今 let 聲明以前使用變量會拋出錯誤。

關於 let 的聲明提高還有一個叫暫時死區的名詞,其實這個詞很是好理解,let 聲明的變量在被賦值以前是沒法使用的,那麼在變量被賦值以前到變量所在做用域範圍開頭的那段位置稱之爲這個變量的死區,可是這個變量的做用域不會被影響,當它被賦值後,即便是在它被定義的位置以前調用這變量也依然是能夠的,這種不可用是暫時的,因此叫作暫時死區。這裏討論暫時死區沒有太大意義,知道是怎麼回事就行。

聲明提高的優先級

聲明提高的優先級發生在同一做用域中聲明同名變量的狀況下,因此let,const,class 聲明的變量就不須要討論這個問題了。只需考慮 var 和 function 聲明的變量。

對於同名的 var 聲明,Javascript 採用的是忽略原則,後聲明的會被忽略,變量聲明和賦值操做能夠寫在一塊兒,可是隻有聲明會被提高,提高後變量的值默認爲undefined,結果是在賦值操做執行前變量的值必爲undefined

對於同名的 function 聲明,Javascrip t採用的是覆蓋原則,先聲明的會被覆蓋,由於函數在聲明時會指定函數的內容,因此同一做用域下一系列同名函數聲明的最終結果是調用時函數的內容和最後一次函數聲明相同,考慮下面這段代碼:

function test() {
    a();
    function a() {
        console.log('a1')
    }
    function a() {
        console.log('a2')
    }
 }
 test();
複製代碼

打印的結果是a2,說明後面定義的函數覆蓋了前面的函數。

對於同名的函數聲明和變量聲明,採用的是忽略原則,因爲在提高時函數聲明會提高到變量聲明以前,變量聲明必定會被忽略,因此結果是函數聲明有效。考慮下面代碼:

function test1() {
    console.log(typeof a);
    var a = 'aVar';
    function a() {
        
    }
}
test1();

//先聲明函數後聲明變量,證實上邊的例子不是function覆蓋了變量
function test2() {
    console.log(typeof a);
    function a() {
        
    }
    var a = 'aVar';
}
test2()
複製代碼

test1和test2運行的結果均是function,這代表函數的變量聲明是優先的。

綜合上面的描述,函數聲明提高的優先級是高於var 聲明變量提高的。

結論

儘管本文是在討論變量提高,可是我本人並不提倡在變量聲明以前使用變量聲明以前使用變量這一違反人類直覺的作法,在實際編碼中咱們仍是應該先聲明變量後使用它。掌握變量提高有助於咱們更好的理解代碼,避免在遇到提高的用法時沒法理解。

相關文章
相關標籤/搜索