在一個函數中(或類定義中),有用var定義的變數為局部變數,沒有定義就賦值的是全域變數。沒有定義就使用的,IE提示錯誤“'x'未定義”。
好象很簡單,但自己實際去試一下才發現問題多多。
我寫一個函數,然後在VS2005中調試,我就想看一看在開發環境如何看出是局部變數與全域變數。
function abc()
{
var i=0;
a=1;
i=a;
}
abc();
var o1=abc;
alert(o1.i); //提示undefined
alert(a); //提示1
debugger; //快速監視this,但找不到a,只有監視this.a才出現
結果,雖然a是正確地提示出來,但我無法在快速監視this中看到a,只有快速監視this.a才能看到。這裡的this為window。VS2008會如何?(後面,在調試中的局部視窗中可以看到this的東西)
如果用類的方式來寫:
var abc=function()
{
var i;
a=1; //相當於建構函式
i=a;
}
var o1=new abc();
alert(o1.i);
alert(a);
debugger;
得到的結果是一樣的,我都監視不到明顯的類的屬性。
對比我的函數寫法,以及別人的類寫法,發現一個學習點:
var o1=abc; //讓o1等於abc,因此其它地方可以執行o1();也就是執行abc();,
typeof(o1)="function"
var o1=abc(); //執行abc,並把傳回值給o1,typeof(o1)="undefined"
var o1=new abc(); //o1為類abc的執行個體,該代碼也會執行函數abc,不過這美其名曰“建構函式”,typeof(o1)="object"
對比一下,發現還是“外國的月亮圓”,別人的代碼強!
再對比一下,還有function的寫法不一樣,下面的寫法被稱為匿名函數,上面的寫法則是普通寫法,但據說上面寫的同時,javaScript也產生了一個同名對象。
我再找,總算在VS2005中找到了它,以及前面提到的私人變數與全域變數。在“調試”菜單的“視窗”子功能表的“局部變數”中可以看到對應的局部變數。
有了這個視窗,我現在應該敢懷疑這句話“上面寫的同時,javaScript也產生了一個同名對象”,或者我理解錯了作者原意。反正,系統裡面只有一個該名字的對象,其typeof的值為function,而不是該名字的對象有兩個,一個object,一個function。
function比string這些簡單類型更牛的是:function可以這樣
abc.i=2;
而string這些簡單類型就不能這樣了:
var s="OK";
s.i=9; //這樣也不行:s.prototype.i=9;
存取全域變數速度比局部變數要慢得多
用以下網頁來驗證
<html>
<head>
<script>
var a = new Date();
a1();
document.write((new Date() - a) + ":");
a = new Date();
a2();
document.write((new Date() - a) + " ");
a = new Date();
a3();
document.write((new Date() - a) + ":");
a = new Date();
a4();
document.write((new Date() - a) + " ");
//設定全域變數
function a1()
{
var i;
for (i=0; i<100000; i++)
{
b=1;
}
}
//設定局部變數
function a2()
{
var i;
var b;
for (i=0; i<100000; i++)
{
b=1;
}
}
//讀取全域變數
function a3()
{
var i;
c=2;
var b;
for (i=0; i<100000; i++)
{
b=c;
}
}
//讀取局部變數
function a4()
{
var i;
var c=2;
var b;
for (i=0; i<100000; i++)
{
b=c;
}
}
</script>
</head>
<body>
</body>
</html>
執行結果為:109:78 94:62 不是固定這個值,但能說明全域變數比局部變數慢30-40%。看來“存取全域變數速度比局部變數要慢得多”這句話沒錯!
如果要頻繁使用全域變數,可以設定一個局部變數,先把全域變數拷貝過來再使用。
這句會不會又有什麼問題?
如果是向上面的測試代碼,拷貝過來應該是沒有問題的。但如果全域變數是在變化之中的話,拷貝過來會跟著變化嗎?
以下代碼做這個測試:
<html>
<head>
<script>
function a1()
{
var i;
a=2;
var b;
b=a;
a=3;
alert(b); //顯示2
b=4;
alert(a); //顯示3
}
function a2()
{
var i;
a=new Object();
var b;
b=a;
a.i=5;
alert(b.i); //顯示5
b.i=6;
alert(a.i); //顯示6
}
function a3()
{
var i;
a=new Object();
var b;
b=a;
a.i=5;
alert(b.i); //顯示5
a=new Object();
a.i=6;
alert(b.i); //顯示5
}
a1();
a2();
</script>
</head>
<body>
</body>
</html>
測試時發現,如果a是簡單類型,b=a也只是值的拷貝,因此如果全域變數變化了,不會反映到局部變數。如果a是物件類型,b=a就是地址的拷貝,因此如果全域變數下面的屬性變化了,可以反映到局部變數中。但如果是a變成新的對象時,b的引用又不對了。
因此可以說,要想用拷貝全域變數來加快速度,程式員要考慮在使用過程中是否值有沒有被更改,或對象有沒有建立過。
VS進入到with時,會有什麼反應
<html>
<body>
<script>
var i=12;
var a=new Object();
a.i=5;
a.j=6;
with (a)
{
temp=i;
i=j;
j=temp;
}
alert(i);
</script>
</body>
</html>
從代碼中要記住with的格式,後面帶一個圓括弧,中間為想節省的對象,後面再帶一個代碼塊。中間如果有使用對象的屬性,就自動引用,而使用非對象的屬性,則當做外面的普通變數處理。
進入with後,VS好象沒有什麼變化,至少“局部變數”視窗沒有什麼變化。但提示功能中對i的提示,就自動使用a.i的值了。出來後,又繼續提示全域變數i的值了。
這裡看,用於with確實可能造成一定的混亂,難怪JsLint不建議使用。
我現在編寫javascript還沒到會用try...catch的地步,先小試一個:
<html>
<body>
<script>
{
var a=b;
}
catch(e)
{
alert(" e.name:" + e.name + "\n" +
" e.message:" + e.message + "\n" +
" e.number:" + e.number.toString(16) + "\n" +
"e.description:" + e.description);
a=1;
}
finally
{
alert(a);
}
</script>
</body>
</html>
有點像VB中的on error goto xxx。如果按照我的編程原則,對已知的錯誤,要想辦法避免,對未知的錯誤,應該要提示出來,並且程式停止(就是使用系統內建的功能),這樣使用者才會有向你報怨。對無法避免的已知錯誤,才需要使用on error goto xxx。我在JavaScript中太多未知的錯誤,因此至今還沒有哪個需要用到try來防止錯誤重複發生。
這裡會提到try...catch是因為變數e只是在catch作用區內才有效,但我測試時,卻發現事實卻非如此。e好象是全程有效。因此取名不要亂取,不要與其它全域變數重名了。