欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于JavaScript中var聲明變量作用域的推斷

 更新時間:2010年12月16日 22:27:06   作者:  
這個問題其實之前困擾了我很久。如今終于想明白了,特來分享,如果有錯誤的地方,請幫忙指正,我會隨時回來修正滴。
一、迷思!由一段代碼引發(fā)的疑惑
請看如下代碼:
復(fù)制代碼 代碼如下:

for(var i=0;i<3;i++) {
console.log(j+","+k);
for(var j=0;j<3;j++) {
var k = j+1;
}
}
console.log(i);

輸出結(jié)果:
undefined,undefined
3,3
3,3
3
如果你是搞c、java等語言的,可能你會不解,為何j、k這種局部變量可以被作用域外的代碼訪問呢?
如果JavaScript中用var聲明的變量可視為局部變量,那么能訪問到這個變量的作用域就是這個變量的局部作用域。如上例,在console.log行處,依然有j、k的作用域,而循環(huán)外,依然有i的作用域。說到這里,也許我可以武斷的說,JavaScript沒有真正意義的局部作用域。真的嗎?非也!

二、如何獲得真正的局部作用域呢?一個寫法引起了我的注意
大家也許看過JQuery的源碼或者Ext的源碼,也許會對下面的寫法有點熟悉。
復(fù)制代碼 代碼如下:

var a = 3,b=4;
var exports = (function() {
var a = 1,b=2;
return {a:a,b:b};
})();
console.log(""+a+","+b);
console.log(exports.a+","+exports.b);

輸出結(jié)果:
3,4
1,2
很神奇的發(fā)現(xiàn)(其實也不神奇,大家都知道啦)函數(shù)內(nèi)部是有獨立作用域的,即函數(shù)內(nèi)部var聲明的變量,僅在函數(shù)內(nèi)部可以使用。所以各框架各大師都這么寫,防止自身局部變量與外界變量(外層局部變量與全局變量)沖突。
至此,我收回第一條里的武斷推斷,修改一下:
JavaScript以函數(shù)為界,每個函數(shù)內(nèi)部擁有一個局部作用域;任何其他的塊(包括普通代碼塊,for循環(huán)、if、while等代碼塊)不存在局部作用域,使用var聲明的變量可以直接穿過這些代碼塊,可以被外部代碼訪問到。


三、何時報錯,何時undefined?var的聲明機制
看代碼:
復(fù)制代碼 代碼如下:

console.log(a)

輸出結(jié)果:
ReferenceError: a is not defined
輸出結(jié)果:
undefined
復(fù)制代碼 代碼如下:

var exports = (function() {
var a = 1,b=2;
return {a:a,b:b};
})();
console.log(a);

輸出結(jié)果:
ReferenceError: a is not defined
猜想結(jié)論:
每次JavaScript引擎執(zhí)行代碼時,會先掃描作用域中的所有代碼(作用域中的function內(nèi)部的代碼不會掃描),并將所有var聲明的變量記錄下來,在代碼執(zhí)行到賦值之前,這些變量的值為undefined。此后如果訪問變量時,先訪問局部變量,如果沒有這個局部變量就訪問上一層的局部變量(如為閉包,上一層為閉包創(chuàng)建環(huán)境),直到訪問到完全局變量。如果都沒有這個變量,那么拋出異常。


四、題外話:閉包+異步,變量值錯亂!如何確保異步情況下局部變量當前值的傳遞?
還是代碼說話:
復(fù)制代碼 代碼如下:

for(var i=0;i<3;i++) {
setTimeout(function() {
console.log(i);
},1);
}

輸出結(jié)果:
3
3
3
為何?因為在閉包異步執(zhí)行的時候,i始終訪問的是外層作用域的i,由于異步了,所以在執(zhí)行閉包的時候循環(huán)已經(jīng)結(jié)束了,i已經(jīng)為3了,故每一次打印出來的都是3。
那如何解決這個問題呢?我們需要把i轉(zhuǎn)換成局部變量。
嗯,有人會有這種寫法:
復(fù)制代碼 代碼如下:

for(var i=0;i<3;i++) {
var j = i;
setTimeout(function() {
console.log(j);
},1);
}

輸出結(jié)果:
2
2
2
為何?
其實之前已經(jīng)解釋過了,其實j和i的作用域是一樣的。都是外層局部變量,在異步情況下循環(huán)執(zhí)行完成的時候j為2(比i少一次i++);
那該怎么辦呢?(請想象某廣告,(⊙v⊙))。
大家知道,函數(shù)中的參數(shù)也算函數(shù)的局部變量。那么這里有一個辦法,可以將局部變量轉(zhuǎn)換為函數(shù)的實參,這樣就達到值傳遞的效果了。
復(fù)制代碼 代碼如下:

for(var i=0;i<3;i++) {
setTimeout((
function(j){
return function() {
console.log(j);
}
})(i)
,1);
}

輸出
0
1
2
其實說了這么多,代碼寫出來大家就差不多明白了吧,用這種匿名函數(shù)的方式去除了異步情況下變量變化的問題,不過此為本貼的題外話了。

總結(jié):
額。不寫了,我懶,哪天抽空補上。嘿嘿。
其實這些結(jié)論RFC中應(yīng)該都寫了吧。但是啃英文文檔。。。還是算了。。自己推斷了。哈哈莫見笑莫見笑

相關(guān)文章

最新評論