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