js詞法作用域


javascript基礎拾遺——詞法作用域(轉載)

 

  本來是想寫js面向對象筆記(二)關於封裝的,但是在敲實例代碼的時候,發現對作用域這個東西的概念有點模糊,翻閱了犀牛后,有點感覺了,就想着先記錄下此時的感受。

  之所以取名叫做詞法作用域,是這個概念是js中相當基礎也是極為重要的,很多想當然的錯誤或感覺怪異的問題都是和這個東西有關。所以,本文主要說下這個名詞的概念以及討論下他牽扯出來的有關變量、函數、閉包的問題。

  1.由變量開始談

  習慣性先來段代碼:

 

復制代碼
1   var x = "globol value";
2 var getValue = function(){
3 alert(x); //彈出"undefined"
4 var x = "local value";
5 alert(x); //彈出"local value";
6 }
7 getValue();
復制代碼

 

  代碼很簡單,首先定義了一個全局變量x並賦了初值,然后寫了個getValue的方法,之后我們用alert彈出x的值,但是結果是undefined,不是global value也不是local value,這個我們可能會感覺到奇怪。其實理解這個問題的關鍵就是要清楚x的作用域。

  第一個var x中的x是全局變量,說到這里順帶說下,js解釋器在執行任何代碼之前會先創建一個全局對象(global object),全局變量就是相當於這個全局對象的一個屬性。同理,對於getValue這個函數,就會生成一個叫做調用對象的東西,局部變量就是這個調用對象的屬性,例子中第二個var x中的x就是局部變量。

  2.主角登場

  說了以上的東西其實是為了將本文的主角——詞法作用域。這個是何方神聖呢?要理解的話,其實我們可以對比傳統面向對象的(如JAVA、C#)中的變量的作用域,我們知道C#中的變量作用域是塊級的,即這個變量只能活動在定義他的一個直接外界內,如if子句內,for子句內定義的變量外界是無法讀取的。而js中呢,變量卻不是這樣的,在同一個函數內定義的變量其它的成員是可以訪問的。看個例子會清楚很多:

 

復制代碼
1   function test(o){
2 var i = 0;
3 if(typeof o == "object"){
4 var j = 0;
5 for(var k=0; k < 10; k++){
6 document.write(k);
7 }
8 document.write(k); //k是可以被訪問到的,即使他在for子句內
9 }
10 document.write(j); //說明j是可以被訪問到的,即使他在if子句內
11 }
復制代碼

 

  清楚了這一點后,就來理解下js中關於詞法作用域的含義。當定義了一個函數后,當前的作用域就會被保存下來,並且成為函數內部狀態的一部分,這個是很重要的一個概念。下面我們回到開篇的那個例子,當getValue函數被定義的時候,他的作用域被保存起來,還有被加到作用域鏈上,他的上端就是全局執行環境。當調用getValue方法的時候,js解釋器首先會把作用域設置為定義函數的時候的那個作用域(即之前保存那個),接下來,他在作用域的前加上調用對象即getValue這個函數,再在他的上端加上全局對象。暈了沒?還是看下我畫的圖把:

  

  這樣比較清楚了把,這個作用域鏈其實和原型鏈有點相似,也好似在很作用域內找不到就會向上去找。比方說開篇那個例子,找x的時候,(對了這里要先介紹下js的預定義機制,就是js解釋器會先對var定義的變量進行初始化,應該說只是起了定義的作用當沒賦值),會先在本作用域內找,有預定義知可以找到x,但是沒賦值,所以是undefined值。知道了這點我們來知道開篇那個代碼其實是等價於下面這個的:

 

復制代碼
1 var x = "globol value";
2 var getValue = function(){
3 var x;
4 alert(x); //彈出"undefined"
5 x = "local value";
6 alert(x); //彈出"local value";
7 }
8 getValue();
復制代碼

 

  實際上js解釋器做的事情應該是按以上這個例子執行的,所以從另一個角度說,將變量的定義放在開頭這個約定是有意義並且有益處的。

  3.延伸

  清楚了以上關於詞法作用域的概念后,我們就不難理解閉包的概念了,他只是用到了作用域鏈的不可向下性(我取的名詞..),即下面的作用域可以訪問上面的,但上面的不可以訪問下面的。當然這只是構成閉包的一個條件,閉包更重要的還是外部函數持有內部函數的一個嵌套函數的引用,由於閉包不是本文主要要討論的(ps:談封裝的時候會說到),所以只是簡單看下例子:

 

復制代碼
1 function foo(){
2 var age = 10;
3 function boo(){
4 age += 10;
5 return age;
6 }
7 return boo;
8 }
9
10 var tx = new foo();
11 alert(tx()); //20
復制代碼

 

  4.總結

  詞法作用域的討論就先這樣了,我想說得是這個概念還是相當重要的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM