JavaScript本身作為一門簡單的語言,就其變數範圍問題一樣令不少人頭暈,這主要是因為JavaScript閉包的存在。本文不打算深入講解JavaScript變數範圍問題(其實本人也沒有能力能把這一話題講的深入些),也不講“閉包”話題,本文只討論最實用的JavaScript範圍知識點。
一、JavaScript範圍分類
JavaScript就兩種範圍:全域(window)、函數級(function)。函數級(function)不要理解為“塊級(大括弧{}級)”。
二、區分及定義JavaScript全域變數與局部變數
1.1定義在所有函數最外邊,使用或不使用var關鍵字定義的變數都是全域變數。全域變數其實被解析成window對象的一個屬性,所以我們可以以“window.全域變數名”方式訪問它,推薦在沒有必要的情況下直接使用變數名訪問。如下例子示範了全域變數定義最常見的方法: 複製代碼 代碼如下:var msg1='This is message 1';
msg2='This is message 2';
alert(window.msg1); //This is message 1 使用window關鍵字進行訪問
alert(window.msg2); //This is message 2
alert(msg1); //This is message 1 省略window關鍵字的訪問方式
alert(msg2); //This is message 2
function otherFunction(){} //其它一些函數或對象聲明代碼
var otherObject={};
1.2在函數內(局部變數運行時環境)一樣可以定義和擷取全域變數。定義的方法就是不使用var關鍵字,而在局部環境中亦可輕鬆獲得全域變數內容,直接使用全域變數名引用即可。需要注意的是:如果函數內定義了與全域變數同名的局部變數,那麼此時函數體將優先使用自己的局部變數,如果此時你非要使用同名的全域變數,請加上window首碼。舉例如下: 複製代碼 代碼如下:var msg1='This is message 1';
var msg3='This is message 3';
function otherFunction()
{
msg2='This is message 2'; //不使用var關鍵字,其實也是定義一個全域變數
var msg3='Message 3';
alert(msg1); //This is message 1 (函數內當然可以訪問到外面定義的全域變數,再深的函數嵌套一樣能正確獲到這個全域變數,這是JavaScript閉包的其中一種體現)
alert(msg3); //Message 3 (局部變數msg3)
alert(window.msg3); //This is message 3 (使用window首碼訪問同名的全域變數msg3)
alert(this.msg3); //This is message 3 (因為otherFunction ()定義在一個全域的環境中,此時otherFunction ()的this也是指向window,所有你看到window. msg3是等於this. msg3的)
}
otherFunction();
//otherFunction函數外面定義的msg1和裡面定義的msg2依然是全域變數
alert(window.msg1); //This is message 1
alert(window.msg2); //This is message 2
2.1使用var關鍵字,在函數體內定義的變數是局部變數,此變數能供其下面所有語句塊({})及子函數使用。這個變數在這個函數裡任何地方都可以訪問到,但卻不能在這個函數的外面“直接”訪問(閉包允許間接訪問,或代理訪問,此知識點不在本文討論範圍)。舉例如下: 複製代碼 代碼如下:function showMsg()
{
if (true)
{
var msg='This is message';
}
alert(msg); //This is message
}
showMsg();
alert(typeof(msg)); //undefiend
//這裡在if {}大括弧內定義的變數msg還能在if外showMsg()內訪問到,但在showMsg()外則是無法訪問的
2.2父函數的變數可以被子函數訪問,但子函數的變數卻不能被父函數訪問,顯然這與我們一開始說的函數級範圍是相吻合的。這看起來老爸爽快些,兒子吝嗇些。舉例如下: 複製代碼 代碼如下:function showMsg()
{
var MsgA='Message A';
this.setMsg=function(msg)
{
var MsgB='Message B';
alert(MsgA); //Message A (子函數setMsg()可以訪問父函數showMsg()的局部變數MsgA)
}
alert(MsgB); //MsgB未定義 (在父函數中不能訪問其子函數中定義的變數MsgB)
}
var sm=new showMsg();
sm.setMsg('Message string');
三、需要注意的幾個地方及提示
1、為了避免變數混亂或被覆蓋,對於局部變數的定義一定不要忘記加上var關鍵字(必要時我們要變數使用完後主動釋放它,即“變數名=null”),同時建議把所有變數集中定義在每個函數體內的開頭位置。舉例如下: 複製代碼 代碼如下:var msg='Message';
function showMsg()
{
var msg; //這裡即使不小心使用了與全域變數一樣的變數名,也不用擔心覆蓋同名全域變數的問題
var a;
var b;
var c;
for (a=0;a<10;a++){}
this.setMsg=function(){}
}
2、巧用匿名函數,減少命名衝突或變數汙染。如下兩段代碼其實實現了相同的功能,而第一段代碼寫法自己可以在那個匿名函數內大膽用自己想用的變數名等,不用擔心自己定義的變數覆蓋其他人定義或自己其它地方定義的變數。 複製代碼 代碼如下://定義一個匿名函數,然後把代碼丟到這個匿名函數裡面,能有效減少命名衝突或變數汙染,這是常見JS架構的做法
(function()
{
var msg='This is message';
alert(msg);
})();
document.write(msg); //msg未定義 (匿名函數外的其它方法已無法調用msg這個變數)
//-----------------------------
var msg='This is message';
alert(msg);
3、不建議在無須執行個體化的函數內使用this代替window去訪問全域變數。一般情況使用this關鍵字的函數應當作為JavaScript類來處理(我喜歡把“cls”作為類名的首碼)。以下函數如果僅當作普通函數調用一下,就不應該出現this關鍵字,因為這通常是去操作一個全域變數了。例子: 複製代碼 代碼如下:function clsMsg()
{
this.msg='This is default message';
this.showMsg=function()
{
alert(this.msg);
}
}
sMsg=new clsMsg();
sMsg.msg='This is new message';
sMsg.showMsg();
四、相關知識點指引
理解以下相關知識點有助於你更好地認識JavaScript變數範圍,本文暫不詳述,隨後會以單獨篇幅來講,敬請關注。
(1)理解JavaScript“預解析”
(2)JavaScript閉包