動畫:「變量提升」引發的一場"血"案 !

動畫:「變量提升」引發的一場

寫在前邊

某一外包公司小李,剛剛入門前端之後,老闆就讓他寫一段前端 JS 項目代碼,不料,這時之前學過Java的小李遇到了一個問題,對於經常寫 Java代碼的小李來說,這屬於一個靈異事件。項目中的一段代碼如下:

動畫:「變量提升」引發的一場

小李越想越感到納悶,明明我在打印之前,沒有聲明任何的變量呀,爲什麼還能使用未聲明的變量,從而打印出 a 的值呢,是不是我的編譯器出現了問題?遇到問題的小李,越想越奇怪,就又寫了一段測試代碼。

動畫:「變量提升」引發的一場

按理說應該輸出undefined呀,爲什麼會輸出的結果爲 10。小李越想越奇怪,到底哪裏出現問題了。這時老闆過來了,看了看代碼,笑了笑說,你還是去先學學基礎吧。就這樣,小李在網上找到了小鹿的這篇教程,學完之後,才恍然大悟。

思維導圖

動畫:「變量提升」引發的一場

1

什麼是變量提升?

我們首先要弄明白什麼是變量提升?顧名思義,從表面上的意思去分析,代碼在真正的執行之前變量就已經進行了提升聲明。是的,確實是這麼個意思,但是我們後邊的原理部分說的這種所謂的“提升”卻不是真正的提升,而是爲了讓開發者便於理解,纔有了變量提升這以名詞,話說到這,那麼它是如何進行提升的呢?

我們還是用小李的例子,第一個例子如下:

動畫:「變量提升」引發的一場

首先,程序執行,首先將聲明的變量提升到最前邊,a 變量就會被提升到最前邊,但是並不會進行賦值操作,變量提升完畢之後,代碼自上而下順序執行。輸出 a 的值,a 此時沒有被賦值,所以輸出 undefined,繼續執行,a 被賦值 10,執行完畢。

動畫:「變量提升」引發的一場

除此之外,其實不僅變量會提升函數也會被提升。

注意:只有 var 聲明的變量或函數纔會進行提升,而 ES6 中的 let 和 const 不會進行提升,後面會講到。

函數提升:

動畫:「變量提升」引發的一場

函數提升優先於變量提升,函數提升會把整個函數挪到作用域頂部,變量提升只會把聲明挪到作用域頂部。

2

爲什麼需要變量提升?

小李之前有 Java基礎,爲何到了 JS 有這種策略,他想不通爲什麼要這樣,順序執行它不香嗎?

彆着急,我們舉個例子就說明爲什麼 JS 中需要變量提升了。如下代碼所示:

動畫:「變量提升」引發的一場

我們想一想,如果 JS 沒有變量提升,這個程序還是否能夠執行呢?我們在 fn 函數中執行 fn2 函數,如果沒有變量提升,此時的 fn2 還沒有聲明,所以找不到,拋出錯誤。

正是由於 JS 存在變量提升,所以程序執行,fn 和 fn2 函數提升被聲明,然後再去執行下面的代碼,就可以正常通過編譯執行,是不是很香呢?最後得出結論爲,變量提升存在的根本原因就是爲了解決函數間互相調用的情況。

3

變量提升的內部原理

要想知道變量提升的內部原理,我們就深入 JS引擎的工作原理。javascript代碼的執行事實上是分兩個階段進行的。

一旦 JS 創建了詞法環境——程序執行的環境,就會執行第一階段。在第一階段,沒有執行代碼,但是javascript引擎會訪問並註冊在當前詞法環境中所聲明的變量和函數 —— 我們上邊所說的變量和函數聲明的提升。javascript在第一階段完成之後開始執行第二階段,代碼自上而下順序執行。

具體第一階段是如何執行的呢?

1、如果我們聲明的是一個函數環境,那麼我們在詞法環境中創建形參和函數參數的默認值,如果是非函數(全局作用域下),那麼就跳過這個步驟。

2、如果是創建全局或函數環境,就掃描當前代碼進行函數聲明,不會掃描其他函數的函數體。但是不會掃描函數表達式或箭頭函數。如果是塊級作用域的話(ES6 的知識),跳過此步驟。

3、掃描當前代碼進行變量聲明。在函數或全局環境中,找到所有當前函數以及其他函數之外通過var聲明的變量,並找到所有在其他函數或代碼塊之外通過let或const定義的變量。

在塊級環境中,僅查找當前塊中通過 let或 const定義的變量。對於所查找到的變量,如果沒有賦值,則標記爲 undefined;否則,對變量進行賦值。

整個處理過程如下圖:

動畫:「變量提升」引發的一場

我們很多人認爲,變量的聲明提升至函數頂部,函數的聲明提升至全局代碼頂部。但是,我們知道原理之後,並沒有那麼簡單。

聲明:變量和函數的聲明並沒有實際發生移動。只是在代碼執行之前,先在詞法環境中進行註冊。雖然描述爲提升了,並且進行了定義,這樣更容易理解 JavaScript 的作用域的工作原理,但是,我們可以通過詞法環境對整個處理過程進行更深入地理解,瞭解真正的原理。

4

如何解決變量提升問題?

如果我們不想讓變量或函數進行變量提升,我們有沒有好的解決辦法?

有的,在 ES6 新標準中,提供了 let和 const來聲明變量,如果我們用 let和 const在聲明之前使用了 a,程序就會拋出錯誤。我們把這個錯誤叫做暫時性死區,就是說我們不能在變量聲明之前使用變量。

動畫:「變量提升」引發的一場****

那麼問題來了,var和 let/const聲明的變量除了變量提升外,有什麼本質上的區別呢?

第一,var聲明的全局變量會綁定在window對象上,而 let和 const 聲明的變量不會掛在到全局變量 window上。

動畫:「變量提升」引發的一場

動畫:「變量提升」引發的一場

第二,let和 const 區別就是,後者不能進行二次賦值。

5

大廠面試題解析

既然我們學了這麼多有關變量提升的知識,光說不練假把式,直接上大廠面試題,檢測一下你是否真正的掌握了變量提升。

動畫:「變量提升」引發的一場

我們更具上邊小鹿總結的規律,我們把這個大廠的題給梳理一遍。

程序執行,創建一個執行環境(詞法環境),然後下一步將可以進行提升的變量進行提升,我們上邊說過有哪幾類是不能提升的?箭頭函數、表達式。而且函數的提升會優先於變量的提升。說到這裏,你一定知道這個題答案了,很明顯的,答案爲 8 。如果你沒有做對,再將從頭看一下文章哦!

6

小結

小李看到這裏,馬上就明白了項目出現的奇怪現象,後來小李被老闆升職加薪了.....

寫到這裏,對於每個基礎的知識點要打牢,尤其是對於初學者,前期學習來很浮躁,比如:變量提升,知道有這麼一回事,但是他並不知其所以然。有的小夥伴會問,學這麼詳細圖個什麼?

這個問題問的好,之所以小鹿將技術點分享的很詳細,拆解的很散,但是有句話說的好,“授人以魚,不如授人以漁”。我希望更能把這種學習的態度和方法分享給大家,一篇文章不止學到了一個知識點,而是能夠提取出,屬於自己的東西。這對你後期閱讀源碼等有很大的提升和幫助!

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