[JavaScript]使用閉包實(shí)現(xiàn)點(diǎn)擊按鈕切換 toggle
前言
- 我以往在實(shí)現(xiàn)點(diǎn)擊按鈕切換DOM元素樣式的時(shí)候,使用的是在全局范圍內(nèi)定義一個(gè)flag變量,然后用true和false來(lái)對(duì)應(yīng)不同的狀態(tài)。
const btn = document.querySelector('#btn'); //獲取按鈕元素
let flag = false; //flag是全局變量
//事件監(jiān)聽(tīng)綁定點(diǎn)擊事件
btn.addEventListener('click',function(){
if(flag){
//狀態(tài)1
}else{
//狀態(tài)2
}
flag = !flag; //切換狀態(tài)(通過(guò)修改flag的值:true或false)
});
//...如果代碼量很多的話,我們可能在其他地方不小心使用著同樣叫做flag的變量
flag = doSomethingToFlag(); //其他代碼可能對(duì)flag的值進(jìn)行了(意料之外的)修改
- 這個(gè)flag變量其實(shí)本意上只是用在按鈕的點(diǎn)擊事件上,但是flag位于全局范圍內(nèi),其他代碼都可以對(duì)flag的值進(jìn)行修改,從而可能導(dǎo)致意料之外的情況發(fā)生。因此,使用全局變量表示不同狀態(tài)是不可取的做法。
- 最近在學(xué)習(xí)閉包的概念,嘗試著使用閉包實(shí)現(xiàn)點(diǎn)擊按鈕切換不同狀態(tài)。
分析
- 使用閉包我們可以把表示狀態(tài)的flag變成私有變量。
const btn = document.querySelector('#btn'); //獲取按鈕元素
function click(){
let flag = true; //這里的flag不再是全局變量
function closure(){
if(flag){
console.log('on'); //這里表示狀態(tài)1
}else{
console.log('off'); //這里表示狀態(tài)2
}
flag = !flag; //切換狀態(tài)
}
return closure;
}
btn.addEventListener('click',click()); //綁定點(diǎn)擊事件
console.log(flag); //報(bào)錯(cuò):flag is not defined
我們?cè)?code>click()函數(shù)內(nèi)部定義了closure()函數(shù),當(dāng)closure()函數(shù)被返回并在其他地方被使用后,它仍然引用著flag,這就導(dǎo)致了click()函數(shù)被銷(xiāo)毀,但是flag不會(huì)被銷(xiāo)毀,也就是形成了閉包。
- 這里稍微對(duì)代碼做一些改進(jìn),使其復(fù)用性更高,也就是封裝
toggle()函數(shù):
//toggle()函數(shù)接收一個(gè)按鈕和兩個(gè)函數(shù):on()和off();
//其中on()表示第1種狀態(tài)要執(zhí)行的內(nèi)容,off()表示第2種狀態(tài)要執(zhí)行的內(nèi)容
function toggle(btn, on, off) {
//使用閉包
function click() {
let flag = true;
function closure() {
if (flag) {
on();//這里執(zhí)行狀態(tài)1要執(zhí)行的內(nèi)容
} else {
off();//這里執(zhí)行狀態(tài)2要執(zhí)行的內(nèi)容
}
flag = !flag;
}
return closure;
}
//為按鈕綁定點(diǎn)擊事件
btn.addEventListener('click', click());
}
- 將功能封裝好后,就可以很方便的將toggle事件綁定到按鈕上:
const btn = document.querySelectorAll('.btn'); //獲取若干個(gè)按鈕
for (let i = 0; i < btn.length; i++) {//使用for循環(huán)為這些按鈕都綁定toggle事件
//toggle函數(shù)的三個(gè)參數(shù)分別是:按鈕DOM元素、狀態(tài)1要執(zhí)行的內(nèi)容、狀態(tài)2要執(zhí)行的內(nèi)容
toggle(btn[i],
function on() {
//doSomething:這里填入狀態(tài)1要執(zhí)行的代碼
},
function off() {
//doSomething:這里填入狀態(tài)2要執(zhí)行的代碼
});
}
- 經(jīng)過(guò)測(cè)試可以發(fā)現(xiàn),各個(gè)按鈕對(duì)應(yīng)的flag是獨(dú)立的,互不干擾的。這是因?yàn)椋?/li>
- 每一次綁定點(diǎn)擊事件時(shí),
toggle()函數(shù)內(nèi)部的click()函數(shù)占有一定內(nèi)存空間,而函數(shù)調(diào)用后,內(nèi)存被回收,flag由于閉包的特性被保留下來(lái); - 又因?yàn)槊看握{(diào)用
click()函數(shù)開(kāi)辟的是不同的內(nèi)存空間,因此內(nèi)部對(duì)應(yīng)的flag也是不同的,故每一個(gè)按鈕都能和一個(gè)flag一一對(duì)應(yīng),不會(huì)互相干擾。
參考文章
[1] JavaScript閉包 | 菜鳥(niǎo)教程
[2] 閉包 - JavaScript | MDN
[3] JavaScript高級(jí)程序設(shè)計(jì)(第4版) [美] 馬特·弗里斯比(Matt Frisbie)——10.14 閉包

浙公網(wǎng)安備 33010602011771號(hào)