亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

 更新時(shí)間:2010年12月16日 22:27:06   作者:  
這個(gè)問(wèn)題其實(shí)之前困擾了我很久。如今終于想明白了,特來(lái)分享,如果有錯(cuò)誤的地方,請(qǐng)幫忙指正,我會(huì)隨時(shí)回來(lái)修正滴。
一、迷思!由一段代碼引發(fā)的疑惑
請(qǐng)看如下代碼:
復(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等語(yǔ)言的,可能你會(huì)不解,為何j、k這種局部變量可以被作用域外的代碼訪問(wèn)呢?
如果JavaScript中用var聲明的變量可視為局部變量,那么能訪問(wèn)到這個(gè)變量的作用域就是這個(gè)變量的局部作用域。如上例,在console.log行處,依然有j、k的作用域,而循環(huán)外,依然有i的作用域。說(shuō)到這里,也許我可以武斷的說(shuō),JavaScript沒(méi)有真正意義的局部作用域。真的嗎?非也!

二、如何獲得真正的局部作用域呢?一個(gè)寫(xiě)法引起了我的注意
大家也許看過(guò)JQuery的源碼或者Ext的源碼,也許會(huì)對(duì)下面的寫(xiě)法有點(diǎn)熟悉。
復(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í)也不神奇,大家都知道啦)函數(shù)內(nèi)部是有獨(dú)立作用域的,即函數(shù)內(nèi)部var聲明的變量,僅在函數(shù)內(nèi)部可以使用。所以各框架各大師都這么寫(xiě),防止自身局部變量與外界變量(外層局部變量與全局變量)沖突。
至此,我收回第一條里的武斷推斷,修改一下:
JavaScript以函數(shù)為界,每個(gè)函數(shù)內(nèi)部擁有一個(gè)局部作用域;任何其他的塊(包括普通代碼塊,for循環(huán)、if、while等代碼塊)不存在局部作用域,使用var聲明的變量可以直接穿過(guò)這些代碼塊,可以被外部代碼訪問(wèn)到。


三、何時(shí)報(bào)錯(cuò),何時(shí)undefined?var的聲明機(jī)制
看代碼:
復(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í)行代碼時(shí),會(huì)先掃描作用域中的所有代碼(作用域中的function內(nèi)部的代碼不會(huì)掃描),并將所有var聲明的變量記錄下來(lái),在代碼執(zhí)行到賦值之前,這些變量的值為undefined。此后如果訪問(wèn)變量時(shí),先訪問(wèn)局部變量,如果沒(méi)有這個(gè)局部變量就訪問(wèn)上一層的局部變量(如為閉包,上一層為閉包創(chuàng)建環(huán)境),直到訪問(wèn)到完全局變量。如果都沒(méi)有這個(gè)變量,那么拋出異常。


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

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

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

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

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

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

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

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

相關(guān)文章

最新評(píng)論