本系列用了大量的篇幅講解了上下文環境和作用域,有些人反映這兩個是一回兒事。本文就用一個小例子來說明一下,作用域和上下文環境絕對不是一回事兒。
再說明之前,我們們先用簡單的語言來概括一下這兩個的區別。
00 上下文環境:
可以理解為一個看不見摸不著的物件(有若干個屬性),雖然看不見摸不著,但確實實實在在存在的,因為所有的變數都在裡面儲存著,要不然我們們定義的變數在哪裡存?
另外,對於函式來說,上下文環境是在呼叫時建立的,這個很好理解。拿引數做例子,你不呼叫函式,我哪兒知道你要給我傳什麼引數?
01 作用域:
首先,它很抽象。第二,記住一句話:除了全域性作用域,只有函式才能建立作用域。建立一個函式就建立了一個作用域,無論你呼叫不呼叫,函式只要建立了,它就有獨立的作用域,就有自己的一個“地盤”。
02 兩者:
一個作用域下可能包含若干個上下文環境。有可能從來沒有過上下文環境(函式從來就沒有被呼叫過);有可能有過,現在函式被呼叫完畢後,上下文環境被銷燬了;有可能同時存在一個或多個(閉包)。
上面的文字不理解沒關係,且看下面的例子。
第一,除了全域性作用域外,每個函式都要建立一個作用域。作用域之間的變數是相互獨立的。因此,全域性作用域中的x和fn作用域中的x,兩者毫無關係,互不影響,和平相處。
第二,程式執行之前,會生成全域性上下文環境,並在程式執行時,對其中的變數賦值。
第三,程式執行到第17行,呼叫fn(5),會產生fn(5)的上下文環境,並壓棧,並設定為活動狀態。
第四,執行完第17行,fn(5)的返回值賦值給了f1。此時執行上下文環境又重新回到全域性,但是fn(5)的上下文環境不能就此銷燬,因為其中有閉包的引用(可翻看前面文章,此處不再贅述)。
第五,繼續執行第18行,再次呼叫fn函式——fn(10)。產生fn(5)的上下文環境,並壓棧,並設定為活動狀態。但是此時fn(5)的上下文環境還在記憶體中——一個作用域下同時存在兩個上下文環境。
講到這裡,重點已經講出來了,之後的場景這裡就不再贅述了。
目的還是希望大家能通過這個例子,來理清楚上下文環境和作用域的關係。當然,也不是非得像個學院派似的一字一文的把概念說出來,簡單理解一下,對用閉包是有幫助的。
---------------------------------------------------------------------------
本文已更新到《深入理解javascript原型和閉包系列》的目錄,更多內容可參見《深入理解javascript原型和閉包系列》。
另外,歡迎關注我的微博。
學習作者教程:《前端JS高階面試》《前端JS基礎面試題》《React.js模擬大眾點評webapp》《zepto設計與原始碼分析》《json2.js原始碼解讀》