動畫:「變數提升」原理中的變數真的進行提升了嗎?

程式設計師小鹿發表於2019-12-12

寫在前邊

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

動畫:「變數提升」原理中的變數真的進行提升了嗎?

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

動畫:「變數提升」原理中的變數真的進行提升了嗎?

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

思維導圖

動畫:「變數提升」原理中的變數真的進行提升了嗎?

1、什麼是變數提升?

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

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

動畫:「變數提升」原理中的變數真的進行提升了嗎?
首先,程式執行,首先將宣告的變數提升到最前邊,a 變數就會被提升到最前邊,但是並不會進行賦值操作,變數提升完畢之後,程式碼自上而下順序執行。輸出 a 的值,a 此時沒有被賦值,所以輸出 undefined,繼續執行,a 被賦值 10,執行完畢。

動畫:「變數提升」原理中的變數真的進行提升了嗎?
除此之外,其實不僅變數會提升函式也會被提升。

注意:只有 var 宣告的變數或函式才會進行提升,而 ES6 中的 let 和 const 不會進行提升,後面會講到。

函式提升:

動畫:「變數提升」原理中的變數真的進行提升了嗎?

函式提升優先於變數提升,函式提升會把整個函式挪到作用域頂部,變數提升只會把宣告挪到作用域頂部。

3、為什麼需要變數提升?

小李之前有 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 的作用域的工作原理,但是,我們可以通過詞法環境對整個處理過程進行更深入地理解,瞭解真正的原理。

如何解決變數提升問題?

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

有的,在 ES6 新標準中,提供了 let和 const來宣告變數,如果我們用 let和 const在宣告之前使用了 a,程式就會丟擲錯誤。我們把這個錯誤叫做暫時性死區,就是說我們不能在變數宣告之前使用變數。

在這裡插入圖片描述
那麼問題來了,var和 let/const宣告的變數除了變數提升外,有什麼本質上的區別呢?

第一,var宣告的全域性變數會繫結在window物件上,而 let和 const 宣告的變數不會掛在到全域性變數 window上。

動畫:「變數提升」原理中的變數真的進行提升了嗎?

動畫:「變數提升」原理中的變數真的進行提升了嗎?
第二,let和 const 區別就是,後者不能進行二次賦值。

5、大廠面試題解析

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

動畫:「變數提升」原理中的變數真的進行提升了嗎?
我們更具上邊小鹿總結的規律,我們把這個大廠的題給梳理一遍。

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

6、小結

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

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

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

❤️ 不要忘記留下你學習的腳印 [點贊 + 收藏 + 評論]

文章+動畫寫了好幾個小時,不妨點贊支援一下。嘻嘻,你不點贊說明你很自私,你怕那麼好的文章讓別人也看到。開個小小玩笑。

動畫:「變數提升」原理中的變數真的進行提升了嗎?

動畫:「變數提升」原理中的變數真的進行提升了嗎?
作者Info:

【作者】:小鹿

【原創公眾號】:小鹿動畫學程式設計。

【簡介】:和小鹿同學一起用動畫的方式從零基礎學程式設計,將 Web前端領域、資料結構與演算法、網路原理等通俗易懂的呈獻給小夥伴。先定個小目標,原創 1000 篇的動畫技術文章,和各位小夥伴共同努力一起學習!公眾號回覆 “資料” 送一從零自學資料大禮包!

【轉載說明】:轉載請說明出處,謝謝合作!~

相關文章