Javascript進行變量提升和函數提升有什麼用?

       相信大家學習JS時,對於變量提升和函數提升這個會有很多疑惑,爲什麼設計這個,不像其他動態語言一樣,是從上到下逐句依次執行,有人說這個的設計其實是低劣的,或者是語言實現時的一個副作用。它允許變量不聲明就可以訪問,或聲明在後使用在前,不管是新手對於此則很迷惑,甚至許多使用JS多年老手也比較迷惑。

       對於Javascript的“提升”是屬於Javascript的精華還是糟粕?這一直是個有爭議的問題,變量提升和函數提升確實有它的好處,但它跟其他語言比較起來,真的是有點不太符合我們思維的常理,只能這樣說,這個是語言設計的不完美性不過現在ES6針對這個進行了改變,加入了let/const 後,變量Hoisting就不存在了

拋開這些不講,我們單純討論Javascript這樣設計變量提升和函數提升的意義segmentfault有用戶說了如下觀點:

JS拿到一段代碼或一個函數的時候,會有兩步主要操作,即解析與執行。

在解析階段,JS會檢查語法,並對函數進行預編譯。

所以當函數的代碼有語法錯誤的時候,在函數執行前就會報錯(SyntaxError)。

接下來是執行階段,就是逐條解釋每條語句並執行。

弄明白JS函數的兩個階段之後,下面就談談聲明提升的問題。聲明提升就是函數中任何位置所聲明的變量或函數,都會自動“提”到函數的最前面,就好像它們是在函數的開頭聲明的一樣。

爲什麼要提升變量和函數的聲明?表面上看,是因爲作用域。確實,在ES6之前,JS並沒有塊級作用域,所有變量要麼具有全局作用域,要麼具有函數級作用域。

但是進一步思考就會發現,這只是聲明提升的結果,而不能成爲必須要這麼做的理由。後來加入的let變量就是一個例子。

那麼究竟爲什麼要進行聲明提升呢?認爲主要原因有兩點:

聲明提升可以提高性能

前面說過,JS會在函數執行前對其進行語法檢查和預編譯,並且這一操作只會進行一次。之所以要這麼做,一個目的在於提高性能。因爲如果沒有這一步,那麼每次執行函數前都必須重新解析一遍該函數,而這是沒有必要的,因爲函數的代碼並不會改變,解析一遍就夠了。

另外,解析的過程中,還會爲函數生成預編譯代碼。在預編譯時,會統計該函數聲明瞭哪些變量、創建了哪些函數(注:這裏就是聲明提升),並對函數的代碼進行壓縮,去除註釋、不必要的空白等。這樣做的好處是每次執行函數時都可以直接爲該函數分配棧空間(不需要再解析一遍去獲取函數中聲明瞭哪些變量,注:這也是聲明提升的好處),並且代碼執行更快(因爲壓縮而變短了)。兩個好處都會提高執行函數的性能。

容錯性更好

衆所周知,JS是一種腳本語言,在發佈之後很長時間內都沒有爲程序員提供編譯器、調試器、語法檢查器等工具。在很長一段時間內其地位始終是Web頁面的附屬品,僅僅用來給頁面添加一些非必要的動態效果,並且其開發和部署也具有很強的隨意性,未經過調試和測試的代碼比比皆是。直到後來Ajax的出現,這一情況才逐步改變。

在這種情況下,提高JS的容錯就是很有好處的了。而聲明提升可以在一定程度上提高JS的容錯性。看下面的例子:

function foo() {

    console.log(a);

    var a;

}

如果沒有聲明提升,這段代碼就是錯的,但有了聲明提升,這段代碼便可以正常運行。

但是你可能會說,正常代碼不應該這麼寫,就像其他語言,變量肯定要先聲明再使用啊,因此這一點只要稍加註意就能避免,不是嗎?

確實如此,但稍加註意也要投入注意力不是?尤其是在修改別人的代碼的時候,這種在聲明前就使用的情況就更容易發生了。

如果上面的例子無法說服你,下面再看一個更有代表性的例子:

function foo() {

    if (...) {

        var a;

    }

    console.log(a);

}

這種情況更常見了,在寫if語句的時候,我發現我需要一個變量a,於是順手寫了var a = …;,但是到後面我又發現這個變量在if語句外面也會用到,於是我忘記了回頭去把a的聲明提到if外面。當if的條件不滿足的時候,裏面的代碼根本不會執行,如果沒有聲明提升,那麼這時候a將不會存在。

而要在代碼層面完全避免這種情況顯然需要投入更多的注意力纔行了。當然,你可能會說,這個好像不是聲明提升,變量a本來就是在使用前定義的啊。

這麼說沒有錯,但是你不能不承認JS的變量沒有塊作用域這一事實與聲明提升有很大關係。比如,如果把var a換成let a。

這裏兩個觀點:

1.解析和預編譯過程中的聲明提升可以提高性能,讓函數可以在執行時預先爲變量分配棧空間

2.聲明提升還可以提高JS代碼的容錯性,使一些不規範的代碼也可以正常執行


對於我來說,我個人很贊同該用戶的觀點,進行提升可以優化程序,提高性能,如果不是很明白,我們可以從另一方面來看下面這個示例:

var a = 100;
 
while (a-- > 0) {
  var b = a;
}

顯然,在具有“提升”的系統中,所有var的都被提升到頂部,因此在while循環的每次迭代中都沒有變量定義:

var a = 100;
var  b;
 
while (a-- > 0) {
  b = a;
}

這樣我們可以看到,這樣針對函數的執行性能進行了有效優化

最後,對於“提升”是否是一個好的或壞的功能?沒有確切的答案。最重要的是,作爲Javascript使用者,我們應該瞭解這種技術並理解它爲什麼有用的原因。

這裏我推薦一篇文章,看完之後相信你也會有更深的瞭解:http://dmitrysoshnikov.com/notes/note-4-two-words-about-hoisting/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章