讀you don't know js 有感之作用域

作用域這個詞有過一些編程經驗的猿們應該都不陌生,可是深究下去,他到底是什麼呢?

根據本書所述我的理解是:作用域是在程序中儲存變量值和引用的一套規則。但是深究下去,這套規則到底是在哪儲存變量值和引用?使用時如何取得它們?甚至這套規則是如何設置的呢?

通常我們總是遇到js是動態執行語言這種說法,但是實際上js也是一門編譯語言,但是與傳統的編譯語言不同,js不是提前編譯的,編譯結果也不能在分佈式系統中進行移植。籠統來說的話,js的編譯大概分爲三步,分詞/詞法分析,解析/語法分析,代碼生成。

大概解釋一下這三步的內容,語法分析是將字符組成的字符串分解成有意義的代碼塊,或者可以稱其爲詞法單元,通俗說就是對我們寫的代碼進行初步加工,分解成方便語法分析的代碼塊等,當然其中還進行一些更加高層次的事項包括一系列特定的性能優化等步驟,但超出我們的內容範圍就不做深究了;緊接着編譯器會對詞法單元進行檢查是否符合ECMA標準有沒有type、reference等錯誤,如果有的話就報錯,同時將其變成“抽象語法樹”,可以理解爲一個代碼單元tree;最後將抽象語法樹轉換爲引擎可以直接執行的代碼,另外可能還有很多像性能優化、語義分析等的過程纔會執行代碼。簡單來說,整個編譯過程就是對代碼進行一系列變化,爲後來執行做好準備。

好了,跑題了一會,現在讓我們回到作用域的討論上,當然編譯過程其實和我們對作用域還有引擎的理解有很大關係,作用域在編譯過程中要配合編譯器工作,由於js是解釋型語言,並沒有提前編譯,所以你編寫的js代碼在瀏覽器上的行爲其實是非常快速的編譯,然後直接執行,而這個過程的時間與用戶體驗有極大的關聯,所以引擎一般都會被設計的儘量迅速來保證性能最佳。然後讓我們來看一下引擎、作用域和編譯到底處在是一個什麼樣的運行機制。

首先先介紹一下這三者。

引擎:負責整個js程序的編譯以及執行過程。

編譯器:負責編譯過程,包括詞法分析,語法分析以及代碼分析等工作。

作用域:負責收集並維護所有聲明的標識符組成的一系列查詢,其遵循着一套嚴格的規則,確定着代碼訪問修改等一系列行爲的權限。

接下來簡單分析一下

 var a = 2;
過程中所發生的“愛恨情仇”。首先編譯器進行工作,遇見var a,編譯器會向作用域詢問是否在本作用域已經聲明過a變量(LHS查詢),如果不在嚴格模式下且聲明過的話,那麼編譯器會忽略var,如果沒聲明過那麼編譯器會要求在作用域中聲明一個新變量a;其次編譯過程完畢後,編譯器爲引擎提供了供執行用的代碼,引擎會執行 a = 2; 引擎會向作用域詢問是否本作用域存在變量a,如果存在那麼就取得他的引用(RHS查詢),如果不存在引擎會繼續查找(後文會解釋)該變量。最終如果查到了就進行賦值操作,沒查到就會報出異常。

其實總結下來 var a = 2; 這段程序主要就是兩步操作,LHS和RHS查詢,爲了理解作用域的機制,我們還是有必要深入探究一下這兩個編譯術語。簡單來說LHS就是取得值的引用,RHS就是取值,爲了方便記憶可以認爲LHS多是爲了修改變量,RHS多是爲了利用變量值。

這樣下來應該就可以初步理解一些作用域的問題了,接下來我們更深入的討論一個作用域的嵌套問題。還記得前面有個遺留的問題,如何繼續查找呢?

當一個塊或函數嵌套在另一個塊或函數中時就產生了作用域嵌套。在當前作用域中如果無法找到變量,那麼引擎會在上一級作用域中繼續尋找,直到找到該變量或者最外層作用域爲止。


發佈了49 篇原創文章 · 獲贊 31 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章