「这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战」。
闭包应用:循环中闭包的处理方案
- 循环事件绑定
var btnList = document.querySelectorAll('.btn');
for (var i = 0; i < btnList.length; i++) {
btnList[i].onclick = function () {
console.log(`当前点击按钮的索引:${i}`);
};
}
// 无法实现? 每次点击按钮,执行对应的方法,方法中的i不是私有的,而是全局的,而此时全局的i已经是循环结束的5了
- 自定义属性
let btnList = document.querySelectorAll('.btn');
let i = 0;
for (; i < btnList.length; i++) {
// 最开始每轮循环的时候,给每一个按钮对象都设定一个自定义属性myIndex,存储它的索引
btnList[i].myIndex = i;
btnList[i].onclick = function () {
// 每一次点击的时候,基于THIS(当前操作元素)获取之前存放的自定义属性值
console.log(`当前点击按钮的索引:${this.myIndex}`);
};
}
//性能比闭包要好一些,但是也有一些性能消耗(元素对象 & 节点集合 & 绑定的方法 都是开辟的堆内存)
- 闭包的N中方案【含let处理机制】
//解决办法一:闭包解决方案,利用闭包的“保存机制”
//每轮循环的时候,都创建一个闭包(不释放的上下文),闭包中存储自己的私有变量i,并且值是每一轮循环的索引;当点击按钮,执行对应的函数,遇到一个变量i,不要再去全局找了,而是让其去所属的闭包中查找即可...
//@1
var btnList = document.querySelectorAll('.btn');
for (var i = 0; i < btnList.length; i++) {
//循环五次,产生五个不释放的闭包,每一个闭包中,都存在一个私有变量i,变量的值是对应的索引
function(i){
btnList[i].onclick = function () {
console.log(`当前点击按钮的索引:${i}`);
};
}(i)
}
//@2
var btnList = document.querySelectorAll('.btn');
for (var i = 0; i < btnList.length; i++) {
//循环五次,产生五个不释放的闭包,每一个闭包中,都存在一个私有变量i,变量的值是对应的索引
btnList[i].onclick = function (i) {
return function(){
console.log(`当前点击按钮的索引:${i}`);
}
}(i)
}
//@3 NodeList集合本身具备forEach方法,和数组中的类似,都是用来迭代集合中每一项
var btnList = document.querySelectorAll('.btn');
btnList.forEach(function (item, index) {
// 迭代集合中每一项,都把这个回调函数执行,产生一个闭包「因为上下文中创建的小函数,被外层的按钮对象的onclick占用了;每个闭包中有一个私有变量index,存储的是当前这一项的索引」
item.onclick = function () {
console.log(`当前点击按钮的索引:${index}`);
};
});
//@4 let也是基于闭包的方案,只不过利用的是LET会产生块级上下文
let btnList = document.querySelectorAll('.btn');
for (let i = 0; i < btnList.length; i++) {
// 每一轮循环都产生一个私有的块级上下文,里面的内容(函数)被外部占用,也会产生一个闭包;而且每个闭包中,都有一个私有变量i记录索引
btnList[i].onclick = function () {
console.log(`当前点击按钮的索引:${i}`);
};
}
//这样操作i也是全局的,也不会产生块级上下文
let i = 5;
for (; i < 5; i++) {
i++
}
//闭包方案虽然可以解决,但是比较消耗内存
- 事件委托
//终极方案 事件委托
//点击每一个按钮,除了触发按钮的点击事件行为,根据冒泡传播机制,也会把body的点击事件行为触发
document.body.onclick = function (ev) {
let target = ev.target;
if (target.tagName === 'BUTTON' && target.className === "btn") {
// 点击的事件源是按钮
let index = target.getAttribute('data-index');
console.log(`当前点击按钮的索引:${index}`);
}
};
- 循环中的定时器
// 能否实现每间隔一秒输出 0 1 2?
for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000); //等待时间 0ms(5~7ms) 1000ms 2000ms
}
//现在效果是,每间隔1000ms都输出的是3?定时器执行,方法中的i不是私有的,向上级查找就是全局的i[此时全局i已经是循环结束的3]
// @1
for (let i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000);
}
//@2
let i = 0;
for (; i < 3; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
}, i * 1000);
})(i);
}
//@3
const fn = i => {
return function () {
console.log(i);
};
};
let i = 0;
for (; i < 3; i++) {
setTimeout(fn(i), i * 1000);
}
//@4
let i = 0;
for (; i < 3; i++) {
// 设置定时器:
// 参数1:回调函数,到时间执行的方案
// 参数2:等待时间
// 参数3:给回调函数“预先传递”的实参值{底层本质也是闭包 柯理化函数思想}
setTimeout(function (n) {
console.log(n);
}, i * 1000, i);
}