所謂的做用域,能夠簡單理解爲一個能夠讀、寫的範圍(區域),有些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未聲明,要到建立這個函數的那個做用域中取值——是「建立」,而不是「調用」這個函數的做用域中。
若有錯誤,歡迎指正。