javascript範圍鏈(Scope Chain)初探

來源:互聯網
上載者:User

關於js的範圍鏈,早有耳聞,也曾看過幾篇介紹性的博文,但一直都理解的模稜兩可。近日又精心翻看了一下《悟透Javascript》這本書,覺得寫得太深刻,在“代碼的時空”一節裡有一段介紹範圍鏈的地方寥寥數語,回味無窮(其實還是理解的模稜兩可^_^)。現在整理下自己的讀書筆記,順便借鑒網上資源,寫下來。
一、從一個簡單的問題說起
下面的js代碼在頁面中運行顯示什麼結果:

var arg = 1;
function fucTest(arg) {
    alert(arg);
    var arg = 2;
    //alert(arg);
}
fucTest(10);

您的答案是什嗎?沒錯,就是彈出10。我的理解是這樣的,funTest函數有一個形參arg,funTest函數傳入實參10,alert方法把10彈出就是了,囧。
好,問題又來了:var arg = 1;
function funcTest() {
    alert(arg);
    var arg = 2;
}
arg = 10;
funcTest();

答案是什嗎?如果是5年前的我,肯定不會再往下想了,還是10!這麼簡單的問題還用想什麼呀?我的理解是這樣的:funTest函數是一個無參數的函數,函數內部通過alert方法,調用外部(全域)的變數arg,在函數執行前,arg賦值為10,彈出arg值後改變arg值為2,所以彈出值為10。
真的是10嗎?是還是不是?
測試的結果:彈出“undefined”,瀑布汗.
二、理解範圍鏈,從javascript運行機制說起
1、js的運行順序
如果一個文檔流中包含多個script程式碼片段(用script標籤分隔的js代碼或引入的js檔案),它們的運行順序是:
步驟1. 讀入第一個程式碼片段(js執行引擎並非一行一行地執行程式,而是一段一段地分析執行的)
步驟2. 做文法分析,有錯則報語法錯誤(比如括弧不匹配等),並跳轉到步驟5
步驟3. 對var變數和function定義做“預解析”(永遠不會報錯的,因為只解析正確的聲明)
步驟4. 執行程式碼片段,有錯則報錯(比如變數未定義)
步驟5. 如果還有下一個程式碼片段,則讀入下一個程式碼片段,重複步驟2
步驟6. 結束
上面的分析已經足夠清楚,步驟二、三和步驟四裡的紅色字型可能是我們新手理解上的一個盲點,尤其是步驟三的“預解析”,如果不清楚什麼叫預解析,總覺得不踏實。而步驟四的“有錯則報錯”也是經常碰到的。舉例來說:function funcTest() {
    alert(arg);
    var arg = 2;
}
funcTest();

上面這段代碼執行時,彈出“undefined”,也就是說arg沒有定義,js的變數不是不用定義也可以嗎?
2、文法分析和“預解析”
(1)、從解釋型語言的編譯過程說起
眾所周知,javascript是解釋型語言,它不同於c#和java等編譯型語言。對於傳統編譯型語言來說,編譯步驟分為:詞法分析、文法分析、語義檢查、代碼最佳化和位元組產生;但對於解釋型語言來說,通過詞法分析和文法分析得到文法樹後,就可以開始解釋執行了。
a、詞法分析
簡單地說,詞法分析是將字元流(char stream)轉換為記號流(token stream)。
但是這個轉換過程並不是可以用一句話就可以概括的那麼簡單,我們可以試著用虛擬碼理解一段簡單的程式:

代碼var result=x-y;的轉換大致可以表示如下:

NAME "result"
EQUALS
NAME "x"
MINUS
NAME "y"
SEMICOLON

b、文法分析
簡單地說,文法分析就是為了構造合法的文法分析樹,而文法分析樹可以直觀地表示出推導的過程。
那麼什麼是文法分析樹?簡單地說,就是程式推導過程的描述。但是到底什麼是文法樹,請參考專業文章,本篇略過。
c、其他
通過文法分析,構造出文法分析樹後,接下來還可能需要進一步的語義檢查。對於傳統強型別語言來說,語義檢查的主要部分是類型檢查,比如函數的實參和形參類型是否匹配等等。
結論:通過上面的分析可以看出,對於javascript引擎來說,肯定有詞法分析和文法分析,之後可能還有語義檢查、代碼最佳化等步驟,等這些編譯步驟完成之後(任何語言都有編譯過程,只是解釋型語言沒有編譯成二進位代碼),才會開始執行代碼。
(2)、執行過程
a、javascript的範圍機制
通過編譯,javascript代碼已經翻譯成了文法樹,然後會立刻按照文法樹執行。
進一步的執行過程,需要理解javascript的範圍機制:詞法範圍(lexcical scope)。通俗地講,就是javascript變數的範圍是在定義時決定而不是執行時決定,也就是說詞法範圍取決於源碼,編譯器通過靜態分析就能確定,因此詞法範圍也叫做靜態範圍(static scope)。但需要注意,with和eval的語義無法僅通過靜態技術實現,所以只能說javascript的範圍機制非常接近詞法範圍(lexical scope).
javascript引擎在執行每個函數執行個體時,都會建立一個執行環境(execution context)。執行環境中包含一個調用對象(call object), 調用對象是一個scriptObject結構(scriptObject是與函數相關的一套靜態系統,與函數執行個體的生命週期保持一致),用來儲存內部變數表varDecls、內嵌函數表funDecls、父級引用列表upvalue等文法分析結構(注意varDecls和funDecls等資訊是在文法分析階段就已經得到,並儲存在文法樹中。函數執行個體執行時,會將這些資訊從文法樹複製到scriptObject上)。
b、javascript範圍機制的實現方法
詞法範圍(lexical scope)是javascript的範圍機制,還需要理解它的實現方法,就是範圍鏈(scope chain)。範圍鏈是一個name lookup機制,首先在當前執行環境的scriptObject中尋找,沒找到,則順著upvalue到父scriptObject中尋找,一直lookup到全域調用對象(global object)。
現在回過頭來分析第二個問題:

var arg = 1;
function funcTest() {
    alert(arg);
    var arg = 2;
}
arg = 10;
funcTest();

在執行funcTest函數時,也即進入了funcTest對應的範圍,js引擎在執行時,當遇到對變數名或者函數名的使用時,會首先在當前範圍(也即funcTest對應的範圍)尋找變數或者函數(顯然,arg變數在funcTest對應的範圍裡被定義為var arg=2 所以alert方法的參數採用的是當前範圍的arg,但是因為arg被定義在alert方法後,所以arg變數預設值為undefined)。當然,如果沒有找到就到上層範圍尋找,依此類推(範圍範圍可以持續到javascript運行環境的根:window對象)。
最後,讓你看的更清楚,上面的代碼其實可以等價於:

var arg = 1;
function funcTest() {
    var arg; //預設值undefined
    alert(arg);
    arg = 2;
}
arg = 10;
funcTest();

c、閉包(closure)
當一個函數執行個體執行時,會建立或關聯到一個閉包。 (關於閉包,打算另寫一篇學習筆記)
scriptObject用來靜態儲存與函數相關的變數表,閉包則在執行期動態儲存這些變數表及其運行值;
閉包的生命週期有可能比函數執行個體長。函數執行個體在活動引用為空白後會自動銷毀;
閉包則要等要資料引用為空白後,由javascript引擎回收(有些情況下不會自動回收,就導致了記憶體流失)。
ps:關於“執行過程”這一段比較拗口,名詞很多,不過別被它們嚇住,一旦理解了執行環境(execution context)、調用對象(call object)、詞法範圍(lexical scope)、範圍鏈(scope chain)、閉包(closure)等這些概念,javascript的很多現象都能迎刃而解。
三、結語
通過第二段的分析,對照第一段筆者曾經做出的判斷(你是不是也覺得筆者曾經的分析和結論很幼稚(哪怕有時結果碰巧也對!)?!不是一般的膚淺啊,^_^),你會發現原來javascript還有這麼多“玄機”,而要真正理解精通又談何容易?先“悟透”再說吧。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.