
回复
点击事件的防连击(防止短时间内多次触发点击) 是非常常见的需求,核心目的是避免因用户快速多次点击导致的业务逻辑异常、重复操作或性能问题。例如:表单提交:如注册、登录、支付、评论发布等。若不防连击,可能导致多次提交相同数据。
解决事件的防连击,需要用到闭包,通过以下例子,了解一下闭包。
通过两个for循环,比较一下
test(){
console.log('---开始执行'+DateUtil.format(new Date().getTime(),'HH:mm:ss'))
let index =0;
for (index; index <10; index++) {
setTimeout(()=>{
console.log('---index:'+ index+' '+DateUtil.format(new Date().getTime(),'HH:mm:ss'))
}, index*1000)
}
}
///打印日志:
I ---开始执行15:59:51.60
I ---index:10 15:59:51.61
I ---index:10 15:59:52.62
I ---index:10 15:59:53.62
I ---index:10 15:59:54.61
I ---index:10 15:59:55.61
I ---index:10 15:59:56.61
I ---index:10 15:59:57.61
I ---index:10 15:59:58.61
I ---index:10 15:59:59.61
I ---index:10 16:00:00.61
test2(){
console.log('---开始执行'+DateUtil.format(new Date().getTime(),'HH:mm:ss.fff'))
for (let index = 0; index < 10; index++) {
setTimeout(()=>{
console.log('---index:'+ index+' '+DateUtil.format(new Date().getTime(),'HH:mm:ss.fff'))
}, index*1000)
}
}
///打印日志:
I ---开始执行16:00:07.114
I ---index:0 16:00:07.116
I ---index:1 16:00:08.115
I ---index:2 16:00:09.114
I ---index:3 16:00:10.115
I ---index:4 16:00:11.115
I ---index:5 16:00:12.114
I ---index:6 16:00:13.115
I ---index:7 16:00:14.115
I ---index:8 16:00:15.114
I ---index:9 16:00:16.115
1.for循环是同步执行的,会瞬间完成所有定时器注册,导致所有打印几乎同时执行,因此打印全局的index是遍历后的最终值
2.let关键字会创建块级作用域,每次循环的i都是独立的,因此test2函数打印的index不是最终值
闭包是指有权访问另一个函数作用域中的变量的函数。即使该函数已经执行完毕,其作用域内的变量也不会被销毁,而是会被闭包捕获并保留。
被闭包捕获的变量不会随外部函数的结束而销毁,而是会在闭包内部持续存在
闭包的基础用法举例:在这个例子中,sumClosure是一个闭包,它捕获了外部作用域中的initialValue变量,并在其内部使用这个变量来计算总和
let initialValue = 10;
let sumClosure = (value: number) => {
return value + initialValue;
};
console.log(sumClosure(10)+''); // 输出 20
闭包的高级用法:闭包也可以用于更复杂的场景,比如作为其他函数的参数或返回值。例如,您可以创建一个函数,该函数接受一个参数,并返回一个基于该参数计算的闭包
function sumClosure2(value1: number){
return (value2: number)=>{
return value1+value2
}
}
console.log(sumClosure2(10)(10)+''); // 输出 20
通过对闭包的基本了解,改造一下上面for循环的打印:
print(num:number){
return ()=>{
setTimeout(()=>{
console.log('---index:'+ num+' '+DateUtil.format(new Date().getTime(),'HH:mm:ss.fff'))
}, num*1000)
}
}
test(){
console.log('---开始执行'+DateUtil.format(new Date().getTime(),'HH:mm:ss.fff'))
let index =0;
for (index; index <10; index++) {
this.print(index)()
}
}
//或者 传参给内部方法
print(){
return (num:number)=>{
setTimeout(()=>{
console.log('---index:'+ num+' '+DateUtil.format(new Date().getTime(),'HH:mm:ss.fff'))
}, num*1000)
}
}
test(){
console.log('---开始执行'+DateUtil.format(new Date().getTime(),'HH:mm:ss.fff'))
let index =0;
for (index; index <10; index++) {
this.print()(index)
}
}
///输出结果
I ---开始执行16:17:47.757
I ---index:0 16:17:47.759
I ---index:1 16:17:48.759
I ---index:2 16:17:49.759
I ---index:3 16:17:50.760
I ---index:4 16:17:51.758
I ---index:5 16:17:52.758
I ---index:6 16:17:53.758
I ---index:7 16:17:54.759
I ---index:8 16:17:55.759
I ---index:9 16:17:56.758
防止在短时间内连续触发点击事件,只需要在每次点击时判断与上次点击时间的时间间隔是否小于阈值,然后做相应的处理即可。因此,我们可以利用闭包,记录每个点击事件的最后点击事件,并且能保证多个点击事件不互相影响,利用了闭包的作用域。
代码实现:
//定义全局放到方法,默认500毫秒,参数类型设为可选参数
export function singleClick(func: Function, interval=500) {
let lastTime = 0;
return () => {
const nowTime = Date.now();
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
lastTime = nowTime;
func();
}
};
}
//点击事件调用
Button('防连击按键1').onClick(singleClick(()=>{
// 点击事件实现。。。
}))
Button('防连击按键1').onClick(singleClick(()=>{
// 点击事件实现。。。
},1000))