<ul>
<li class="id1">這是一個(gè)例子</li>
<li class="id2">這是一個(gè)例子</li>
<li class="id3">這是一個(gè)例子</li>
</ul>
以上的代碼查乒,我們要實(shí)現(xiàn)的一個(gè)功能是,實(shí)現(xiàn)點(diǎn)擊每個(gè) li
標(biāo)簽時(shí)候彈出當(dāng)前點(diǎn)擊的那個(gè) li
標(biāo)簽的索引郁竟,這里我們用的是 javascript
的原生方法玛迄。
剛開(kāi)始的時(shí)候,我自以為是的用了下面的方法棚亩,相信在沒(méi)有了解閉包之前的朋友們應(yīng)該都有這么寫(xiě)過(guò)
var len = document.querySelectorAll('li').length; //獲取li標(biāo)簽的長(zhǎng)度
var obj = document.querySelectorAll('li'); //取得li這個(gè)對(duì)象
for(var i = 0;i < len ; i++){
obj[i].onclick=function(){
alert(i);
};
}
錯(cuò)誤理解蓖议,循環(huán)三次,依次對(duì) li
標(biāo)簽綁定點(diǎn)擊事件讥蟆,在綁定的時(shí)候彈出循環(huán)的索引(此時(shí)循環(huán)的索引是和 li
標(biāo)簽的索引是一致的)勒虾。
現(xiàn)在當(dāng)我們?nèi)c(diǎn)擊的時(shí)候,會(huì)發(fā)現(xiàn)無(wú)論我們點(diǎn)擊那個(gè)標(biāo)簽瘸彤,彈出的都是3,顯然這不是我們?nèi)魏我粋€(gè)表現(xiàn)的索引修然,但是我們發(fā)現(xiàn)我們標(biāo)簽的長(zhǎng)度是3,最大的索引是2,而這里的3正好是 for
跳出循環(huán)的值质况。為了驗(yàn)證這個(gè)說(shuō)法愕宋,我們逐步次添加標(biāo)簽,再次證明了我們的說(shuō)法结榄。
為什么我們?cè)邳c(diǎn)擊的時(shí)候掏婶,取不到正確的 i
值?
1495788014(1).jpg
給for
循環(huán)打斷點(diǎn),我們發(fā)現(xiàn)i
確實(shí)經(jīng)歷了從0到2的步驟潭陪,并且給obj[0] obj[1] obj[2]
都依次綁定了點(diǎn)擊事件,到i
到3的時(shí)候最蕾,不滿足循環(huán)條件依溯,所以跳出。這里就引入了我們的另外一個(gè)概念瘟则,同步和異步黎炉。
點(diǎn)擊事件是一個(gè)異步事件,初始化的時(shí)候醋拧,我們綁定了點(diǎn)擊事件慷嗜,但是并沒(méi)有觸發(fā)點(diǎn)擊事件的發(fā)生淀弹。因?yàn)槭屈c(diǎn)擊時(shí)異步執(zhí)行的,javascript是單線程庆械,需要把for
循環(huán)執(zhí)行完畢薇溃,當(dāng)我們觸發(fā)點(diǎn)擊事件的時(shí)候,此時(shí)i
值已經(jīng)到達(dá)了3缭乘,之前的i
并沒(méi)有被保存下來(lái)沐序,跳出了判斷。我們要做的就是堕绩,在綁定事件的時(shí)候策幼,也把綁定時(shí)的值傳到事件內(nèi)部。
在一個(gè)函數(shù)內(nèi)部奴紧,添加一個(gè)函數(shù)特姐,內(nèi)部函數(shù)可以訪問(wèn)外部函數(shù)的變量(相反,外部函數(shù)不可以訪問(wèn)內(nèi)部函數(shù))黍氮,這就形成了一個(gè)閉包唐含。
var len = document.querySelectorAll('li').length;
var obj = document.querySelectorAll('li');
console.log(document.querySelectorAll('li').length);
for(var i = 0;i < len ; i++){
(function(i){
obj[i].onclick=function(){
alert(i);
};
})(i);
}
這里通過(guò)自執(zhí)行的匿名函數(shù),就實(shí)現(xiàn)了每次循環(huán)的時(shí)候滤钱,把i
值傳到事件內(nèi)部觉壶。循環(huán)給obj
綁定事件的時(shí)候,就把i
給保存了下來(lái)件缸。
閉包的深入理解
游覽器是實(shí)現(xiàn)時(shí)就認(rèn)為只要存在訪問(wèn)上層作用域的可能性铜靶,就會(huì)被當(dāng)成是一個(gè)閉包。
總結(jié)一下閉包:
- 閉包是在函數(shù)被調(diào)用執(zhí)行的時(shí)候才被確認(rèn)創(chuàng)建的他炊。
- 閉包的形成争剿,與作用域鏈的訪問(wèn)順序有直接關(guān)系。
- 只用內(nèi)部函數(shù)訪問(wèn)了上層作用域鏈中的變量對(duì)象時(shí)痊末,才會(huì)形成閉包蚕苇,因此我們可以利用閉包來(lái)訪問(wèn)函數(shù)內(nèi)部的變量。
Array.proyotype.map
這個(gè)方法可以很方便的解決上面的問(wèn)題凿叠,而不用用到閉包的概念
var arr = [obj[0],obj[1],obj[2]];
arr.map(function(currentValue,index,array){
currentValue.onclick=function(){
alert(index);
}
});
因?yàn)檫@里這個(gè)方法的其中一個(gè)參數(shù)就是索引涩笤,合理的避開(kāi)了上面的問(wèn)題。當(dāng)然這里的前提是要先把每個(gè)對(duì)象轉(zhuǎn)成數(shù)組盒件。因?yàn)檫@是在Array
對(duì)象原型上的方法蹬碧。
var len = document.querySelectorAll('li').length;
var obj = document.querySelectorAll('li');
console.log(document.querySelectorAll('li').length);
var arr = [];
for(var i = 0;i < len ; i ++){
arr.push(obj[i]);
}
arr.map(function(currentValue,index,array){
currentValue.onclick=function(){
alert(index);
}
});