所謂的作用域,可以簡單理解為一個可以讀、寫的範圍(區域),有些js經驗的同學可能會說:"js沒有塊級作用域",js除了全局作用域外,只有函數可以創建作用域。作用域的一個好處就是可以隔離變數。 我們通過一些例子來幫助我們理解js中的作用域。 如果對作用域一點不瞭解的同學可能會說 alert的是1或者報 ...
所謂的作用域,可以簡單理解為一個可以讀、寫的範圍(區域),有些js經驗的同學可能會說:"js沒有塊級作用域",js除了全局作用域外,只有函數可以創建作用域。作用域的一個好處就是可以隔離變數。
我們通過一些例子來幫助我們理解js中的作用域。
1 alert(a); 2 var a = 1;
如果對作用域一點不瞭解的同學可能會說 alert的是1或者報錯;但實際上是undefined;
說到這裡,我們首先說一下js逐行解析代碼之前做的一些準備工作,
js在逐行讀代碼之前,會做一些“預解析”工作,會先提前找到一些”小東西”,當然”js解析器“不會隨便找一些數據的,它會根據var,function,參數來找。
”js解析器“它比較”懶“,在正式運行代碼之前都會給var聲明的變數賦值為undefined,也就是var a = undefined;會把整個函數看作一個代碼塊,不去管裡邊有多少代碼。參數等到後邊例子中會說。
當所有準備工作都做好後,“JS解析器”就開始逐行執行代碼了,現在我們來分析開始的這個例子就很容易明白為什麼是undefined了。
再來看下邊這個例子
1 alert(a); 2 var a = 1; 3 alert(a); 4 var a = 2; 5 alert(a);
我們來一點點分析這個
首先 ”預解析“: 解析器會找var
讀到第二行時 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未聲明,要到創建這個函數的那個作用域中取值——是“創建”,而不是“調用”這個函數的作用域中。
如有錯誤,歡迎指正。