Javascript解析之作用域理解

atlantisholic發表於2011-07-11

學習任何程式語言,都逃不過理解變數的作用域。

在javascript中,變數的作用域有全域性(window物件)作用域和函式呼叫作用域。


以下變數具有全域性作用域
1. 所有在最外層定義(非函式體內定義)的變數都擁有全域性作用域
2. 所有末定義直接賦值的變數,系統會自動宣告為擁有全域性作用域的變數
3. 所有window物件的屬性擁有全域性作用域


以下變數具有函式作用域
1. 在函式體內部用var定義的變數,這裡要注意一點,只要是在函式裡定義的變數,就算是在最後一句定義,該變數也擁有整個函式的作用域。

 

特別應該說明的一點是,作用域是層層包含的,最外層是全域性作用域,裡面可以包含函式呼叫作用域,函式呼叫作用域裡面還可以再有函式作用域,下面,我們看一個簡單的例子:

<!-- var scope = "global"; window.x = "x-global"; var f1 = function(){ var scope = "function1"; y = "y-golbal"; alert(scope); //will display function1 alert(window.scope); //will display global alert(x); //will display x-global } f1(); alert(scope);//will display global alert(y); //will display y-golbal // --&gt

 

這個例子說明了簡單的全域性作用域和函式作用域的區別,我們再思考一下問題,如果函式f1裡面沒有變數scope,而全域性作用域裡面有這個變數,會有什麼情況呢?

<!-- var scope = "global"; var f1 = function(){ //var scope = "function1"; alert(scope); //will display global alert(window.scope); //will display global } f1(); alert(scope);//will display global // --&gt

 

為什麼會這樣呢,這是由於javascript的尋找變數的機制造成的。上面己經說了,作用域是層層包含的,上面的這個例子的層次應該是這樣的: 全域性作用域->f1的作用域。而javascript查詢一個變數時,會從當前作用域往上找,直到找到為止,所以這個例子中,先在f1的作用域找scope,找不到,則到外層的全域性作用域找,在全域性作用域中找到了scope,就使用該變數,這個例子中,如果在全域性作用域也找不到變數scope,則會報錯。
下面我們把這個例子改一下,就會有一個很有趣的現象

<!-- var scope = "global"; var f1 = function(){ alert(scope); //will display undefined alert(this.scope); //will display global var scope = "function1"; //declare scope here } f1(); alert(scope);//will display global // --&gt

 

這裡還是用javascript查詢變數的機制來解釋,首先在f1的作用域裡面找,而f1定義了該變數,所以使用該變數,但是還末賦值,所以是undefined。這裡要說明一點,就是作用域內的變數不管在函式的哪裡宣告,javascript都會在函式執行前在作用域內包含該變數。
下面,我們再看複雜一些的例子:

<!-- var scope = "global"; var scope2 = "global-scope2" var f1 = function(){ var scope = "function1"; var scope2 = "function1-scope2"; (function(){ //function2 var scope = "function2"; (function(){ //function3 alert(scope); //will display function2 alert(scope2); //will display function1-scope2 })(); })(); } f1(); // --&gt

 

 

下面,我們還是以javascript查詢變數的機制來解釋一下。首先,我們看看作用域的包含情況: 全域性作用域-> f1的作用域 ->f2的作用域-> f3的作用域。對於變數scope,先在f3作用域內找,沒找到,往外,在f2作用域裡找到,使用該值,所以打出的值為 function2。對於scope2,先在f3找,沒找到,住外,在f2找,還是沒找到,再往外,在f1找,找到,使用該值,所以打出的值為function1-scope2。

上面的函式都是在定義該函式的作用域裡呼叫,如f3 在f2定義並呼叫,f1在全域性作用域裡定義並在全域性作用域呼叫,那麼,如果在全域性作用域呼叫,或是在別的函式裡呼叫f3會有什麼情況呢?下面我們試一下:

<!-- var scope = "global"; var scope2 = "global-scope2" var f1 = function(){ var scope = "function1"; var scope2 = "function1-scope2"; return (function(){ var scope = "function2"; return function(){ alert(scope); //will display function2 alert(scope2); //will display function1-scope2 } })(); } var f4 = function(fun) { fun(); } var f3 = f1(); f4(f3); // --&gt

 

上面的例子我們返回f3,並在f4裡面呼叫f3,但結果沒變,為什麼呢?這是因為javascript的作用域包含關係是在函式定義的時候確定的,而不是在呼叫的時候確定的,所以不管在哪呼叫f3,函式的作用域包含關係都是: 全域性作用域->f1->f2->f3。不過上面的這個例子還有另外一個作用域包含關係: 全域性作用域->f4的作用域。

最後,還要說明的是,javascript沒有塊作用域,這點跟java,c++,c#,php等都是不同的,所以在迴圈語句裡面建立的變數也是擁有函式呼叫作用域或是全域性作用域的,並不會有臨時變數存在。

注:
1.在javascript裡,函式也是可以作為資料傳遞的,在上面的例子裡用到了函式作用資料傳遞
2.上面的例子用到了閉包的知識,不清楚的朋友可以瞭解一下相關的知識

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23071790/viewspace-701877/,如需轉載,請註明出處,否則將追究法律責任。

相關文章