淺談javascript中的做用域

所謂的做用域,能夠簡單理解爲一個能夠讀、寫的範圍(區域),有些js經驗的同窗可能會說:"js沒有塊級做用域",js除了全局做用域外,只有函數能夠建立做用域。做用域的一個好處就是能夠隔離變量。函數

咱們經過一些例子來幫助咱們理解js中的做用域。spa

1 alert(a);
2 var a = 1;

若是對做用域一點不瞭解的同窗可能會說 alert的是1或者報錯;但其實是undefined;code

說到這裏,咱們首先說一下js逐行解析代碼以前作的一些準備工做,blog

js在逐行讀代碼以前,會作一些「預解析」工做,會先提早找到一些」小東西」,固然」js解析器「不會隨便找一些數據的,它會根據var,function,參數來找。作用域

」js解析器「它比較」懶「,在正式運行代碼以前都會給var聲明的變量賦值爲undefined,也就是var a = undefined;會把整個函數看做一個代碼塊,不去管裏邊有多少代碼。參數等到後邊例子中會說。io

當全部準備工做都作好後,「JS解析器」就開始逐行執行代碼了,如今咱們來分析開始的這個例子就很容易明白爲何是undefined了。function

再來看下邊這個例子class

1 alert(a);
2 var a = 1;
3 alert(a);
4 var a = 2;
5 alert(a);

咱們來一點點分析這個變量

首先 」預解析「: 解析器會找varim

讀到第二行時  a = undefined;

讀到第四行時 依然  a = undefined;

正式逐行執行代碼:

第一行 alert:undefined 

第二行  a = 1;

第三行 alert:1;

第五行 alert:2

接着看下邊這個例子

1 alert(a);                    
2 var a = 1;
3 alert(a);                    
4 function a (){ alert(2); }
5 alert(a);                    
6 var a = 3;        
7 alert(a);                    
8 function a (){ alert(4); }
9 alert(a);    

咱們依然來一點點分析這個

首先 」預解析「: 解析器會找var function;

讀到第二行時 a = undefined;

讀到第四行時 a = function a (){ alert(2);} //全部的函數,在正式運行代碼以前,都是整個函數塊;變量遇到重名的,只留一個變量,若是變量和函數重名,就只留下函數。

讀到第六行時,a = function a (){ alert(2);}

讀到第八行時,a = function a (){ alert(4);}

正式逐行執行代碼:

第一行 alert: function a (){ alert(4);} 

第二行  a = 1; //表達式能夠修改預解析的值!

第三行 alert:1;

第四行 函數沒有調用,略過;

第五行 alert:1;

第六行 a = 3;

第七行 alert:3

第八行 函數沒有調用,略過;

第九行  alert:3

如圖所示:

 

繼續看例子:

1 var a = 1;
2 function fn1(){
3     alert(a);      //undefined                   
4     var a = 2;
5 }
6 fn1();
7 alert(a);   //1

首先 」預解析「: 解析器會找var function

讀到第一行時 a = undefined;

讀到第二行時 fn1 = function fn1 (){alert(2);var a = 2;}                             

正式逐行執行代碼: 第一行 a = 1;

第六行 函數調用,進入函數做用域 在函數做用域內依舊是先預解析,再逐行執行

  函數內預解析:a = undefined;

  執行:alert:undefined;

  a = 2;  //此時的a僅爲函數做用域中的a,不會影響全局中的a

函數執行完畢,回到全局做用域;

第七行 alert:1;

繼續:

1 var a = 1;
2 function fn1(){
3     alert(a);       //1             
4     a = 2;
5 }
6 fn1();
7 alert(a);    //2

這個例子上邊那個例子惟一的區別就是函數中的a沒有var,只分析其中關鍵的地方

在函數做用域中 第三行alert(a),因爲函數中沒有var a,因此"解析器"會到函數的做用域的上一級做用域去尋找a(做用域上下級關係的肯定就看函數是在哪一個做用域下建立的,在哪一個做用域下建立,就是哪一個做用域的下級),此時函數的上一級是全局做用域,在全局做用域中,a = 1,因此此時第三行 alert:1,接着第四行,a = 2賦值,依然是函數做用域中沒有a, 因此在上一級做用域,也就是全局做用域中找到a,修改全局做用域中的a, 因此會使全局做用域中的a = 2, 所以第七行 alert:2;

這點要理解清楚,注意有無var的區別。

接着來:

1 var a = 1;
2 function fn1(a){
3     alert(a);     //undefined                
4     a = 2;
5 }
6 fn1();
7 alert(a);   // 1

這個例子和上一個的區別就是多了個參數,參數的做用至關於局部變量,也就是在函數中預解析會有var a = undefined,因此第三行 alert:undefined,第四行 a = 2 改的是函數做用域中的a,不影響全局中的a,第七行alert:1;

接着:

1 var a = 1;
2 function fn1(a){
3     alert(a);                        // 1
4     a = 2;
5 }
6 fn1(a);
7 alert(a);          // 1

這個例子又與上一個有些區別,在第六行函數調用時傳了個實參進去,第六行函數實參的a是全局變量a = 1的1,函數執行時,第二行 a = 1,因此第三行alert:1,第七行alert:1。

注意這幾個例子之間的區別,別混淆了。

再來一個:

1 var a = 1;
2 function en(){
3     var a = 2;
4     fn();
5 }
6 function fn(){
7     alert(a);           //1
8 }
9 en();

fn中的a未聲明,要到建立這個函數的那個做用域中取值——是「建立」,而不是「調用」這個函數的做用域中。

 

若有錯誤,歡迎指正。

相關文章
相關標籤/搜索