前言

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的值进行了(意料之外的)修改

分析

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()函数接收一个按钮和两个函数: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());
}
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要执行的代码
	});
}
  1. 每一次绑定点击事件时,toggle()函数内部的click()函数占有一定内存空间,而函数调用后,内存被回收,flag由于闭包的特性被保留下来;
  2. 又因为每次调用click()函数开辟的是不同的内存空间,因此内部对应的flag也是不同的,故每一个按钮都能和一个flag一一对应,不会互相干扰。

参考文章

[1] JavaScript闭包 | 菜鸟教程
[2] 闭包 - JavaScript | MDN
[3] JavaScript高级程序设计(第4版) [美] 马特·弗里斯比(Matt Frisbie)——10.14 闭包

发表回复