對一個js塊做用域問題的思考

1.問題



首先把問題放出來,昨天看了一個掘友發的一個問題,而後跟我同事一塊兒研究了一下,沒找出來是爲何,而後我回來一直在想爲何,而後各類找資料研究,從各個方面找爲何,好比js上下文,做用域,js垃圾回收,堆棧調用狀況等等。javascript

2.js斷點調試找答案

首先若是不看上面的圖,以你如今知道的js知識,你以爲打印出來應該是什麼。第二張圖其實打印出來的結果在乎料之中,緣由就是函數聲明提高,沒問題,可是第一張圖爲何呢?這裏能夠發散一下思惟,好比說是否是在塊做用域中,變量和函數之間存在某種互相覆蓋的問題啊,或者說先在塊中聲明的會被掛載到全局的window對象下面,後面聲明的就掛載不上去了,而且不會覆蓋,而後能夠把代碼稍微改改,驗證一下你的思想,頗有意思。而後下面咱們斷點調試看下:html


首先進花括號一步都沒走的時候,可是a和b已經掛載到全局變量的window對象下面了,這就說明代碼塊中隱式聲明的變量是全局變量(這句話不對,此時掛載到window對象下面的,實際上是函數掛的,並非隱式變量是全局變量的緣由掛上去的參考文章這篇文章,基本是這個問題的答案了,解釋了爲何,連接: juejin.im/post/5d90ae…),代碼至關於這樣:


此時咱們再看a在塊做用域中就已是方法了,注意此時我function a(){}這段代碼還沒走完呢,這就說明函數聲明在js解析(注意是解析不是執行)的時候被提高到了代碼塊頂部,進花括號的那一刻起,函數就已經被聲明瞭,咱們再往下面一步走java


此時a無論在塊做用域仍是全局做用域中都變成了a函數,那這裏是否是能夠理解爲運行上面一行代碼,而後就給全局變量下的a賦值爲函數呢,咱們再看下一步git


a=50;走完以後,也就出了塊做用域,此時咱們看到沒有了塊做用域,由於已經出了塊做用域了,而後全局對象window裏面a仍是函數,並非50,可是若是你在塊做用域a後面加一行的斷點看的話,此時塊做用域裏面的a的值爲50,問題就在這裏,爲何此時塊做用域裏面的a的值跟全局window對象下面的值結果不同呢?es6



而後咱們再往下走一步:github


而後進第二個塊做用域,發現跟前面進第一個塊做用域同樣,還沒執行第一行,塊做用域裏面的b已是函數了,緣由也跟第一個同樣js解析的時候函數聲明提高,而後咱們再往下走一步:web


這一步走完咱們發現塊做用域裏面的b已經變成50了,可是全局window對象下面的b仍是undefined,這我也不知道爲何,那我也就只能說此時b是定義在塊做用域中的內部變量了,再往下走一步bash


可是當我走出塊做用域的時候,b居然在全局對象下變成了50,那就證實我上面說的不對,b不是塊做用域中的內部變量,由於此時執行完方法立馬就出塊做用域了,咱們看的不是很清楚,咱們在方法下面加一行代碼,方便調試看結果:函數


確實是當我b函數那一步走完,塊做用域和全局對象window下面的b都變成了50,那我這裏我就認爲是函數b在js解析的時候就被提高到了塊做用域的最上面,執行到b函數那一步其實在以前就已經執行過了,至關於js執行的時候代碼變成下面這樣:post

{
    function b() {};
    b = 50;
}複製代碼

咱們再看這個代碼不正是上面a那一個塊做用域的代碼嗎,因此在塊做用域中js執行的時候上下兩個塊做用域中是同樣的,因此在塊做用域中打印a,b獲得的結果都是50,而後下一步:


出了塊做用域,就只有全局對象window了,而後window對象下面的b仍是50,因此最後打印出來也是50。走到這一步就全部的步驟都走完了,那麼咱們再回頭看上面的a爲何塊做用域中的值跟window對象下面的a的值不同,經過走完下面一個代碼塊咱們發現上面代碼塊跟下面代碼塊只有函數放的位置不同,結果就不同,那咱們就看一下這裏函數聲明提高究竟是怎麼提高的。

3.塊做用域中的函數聲明提高

而後我就找到阮一峯博客裏面寫的關於es6塊級做用域的文章:

es6.ruanyifeng.com/#docs/let#%…

另外一篇關於js變量的生命週期的文章:

dmitripavlutin.com/variables-l…

在第一篇文章中,看見裏面有真麼一段話:


而後找到這麼一句話,我就點連接進去看:


發現es6規範裏面真的有對代碼塊做用域的一些規定,可是我也沒太看懂,反正能肯定跟這有關係。而後我又點擊了另外一個行爲方式的連接進去看:


而後在這個回答裏找到這麼一個回答,這個回答說代碼塊做用域中定義的函數相似於var定義的變量,(前面阮一峯博客裏也是這麼說的),然而第二個綁定僅在塊內部可見,也就是說,第二個綁定在外面是訪問不到的,那用這段話來解釋咱們代碼的話就是先無論代碼塊中的函數聲明提高,而後從上面往下運行,看見第一個就綁定到全局的window對象上,第二個就只在函數做用域內可見,那這樣的話我若是在代碼塊內部打印,那結果應該是誰在後面定義咱們就打印誰啊,而打印的結果倒是證實了函數提高存在的。因此這個好像也解釋不通。而後從這個回答裏面我又找到一個這個連接:


而後的而後我就不知道該怎麼去看這個問題了,可是我相信應該接近答案了,或許答案就藏在上面es6規範B3.3裏面的某個點。固然也能夠從調用棧,js垃圾回收,js上下文,js引擎執行解析過程,函數與變量聲明建立原理等等各個方面去分析,這應該是一個值得去分析思考的問題,同時也是頗有意思的一個問題,相信你是能學到一些東西的,下面有一些參考連接,若是感興趣能夠研究一下,在日常寫代碼的時候可能永遠也不會遇到,頗有意思的問題。
參考資料:
相關文章
相關標籤/搜索