今天项目上遇到了使用防抖函数的需求,所以查询了一下,在这里统一将节流函数与防抖函数做一下整理,并顺便说下闭包在这的用途。
首先说一下他们各自的作用:
1、防抖函数
使用场景:多次触发,只执行最后一次
2、节流函数
使用场景:多次触发,隔一段时间触发一次
在使用方式上,防抖函数与节流函数非常的相似,我们先来看下我最开始想到的简易版防抖
// 定义一个全局变量,用来判断函数是否已经执行
var timer;
function debounce(fn) {
// 如果timer不为空,说明还没有执行函数,那就清理了上次触发,准备执行下次
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function() {
fn()
},200)
}
节流与防抖是很像的,只是清理未执行函数的时机不同,我们可以看到,防抖函数是在每次触发时都查看下是否有未执行的方法,如果有,直接不执行了,准备下一次;节流函数同样是在每次触发时都检测是否有未执行完成的,只是如果检测到有未执行完的函数,他会等函数执行完才可以被下一次触发,如下:
var timer;
function throttle(fn) {
// 如果timer不为空,说明还没有执行函数,那就拒绝触发,等待函数执行完成
if (timer) {
return
}
timer = setTimeout(function() {
fn()
clearTimeout(timer)
},200)
}
他们的区别应该是比较明显了。
不过这种实现方式是有点问题的,他定义了一个全局的变量,那如果全局的需要使用节流防抖的函数都去调用他的话,timer的使用将会十分混乱,造成无法预知的问题。这个时候,闭包的好处就体现出来了,他可以为每个防抖函数划分一块区域,就不会造成混乱,看下他的代码:
function registDebounce(fn,delay) {
var timer;
var _that = this;
return function () {
var args = arguments; // 接收参数
// 如果timer不为空,说明还没有执行函数,那就清理了上次触发,准备执行下次
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function() {
fn.apply(_that,args);
},delay)
}
}
他的使用方式也发生了改变,从方法名就能看出来,由原来的直接调用,变为了现在的需要先注册再使用。
function print(i) { console.log("print",i) }
let printDebounce = registDebounce(print,200); // 注册防抖
// 调用防抖函数
for(let j = 1; j< 10; j++) {
printDebounce(j);
}
// 只输出 print 9
这样编写的防抖函数,就可以作为一个工具方法提供给全局使用了,因为每次注册的时候,都会给这个方法分配一个单独的空间,timer也是各自空间中的变量,各个方法不会互相影响。
同样可以看下节流函数代码:
function registThrottle(fn,delay) {
var timer;
var _that = this;
return function () {
var args = arguments; // 接收参数
// 如果timer不为空,说明还没有执行函数,那就直接拒绝,等待函数执行完成
if (timer) {
return
}
timer = setTimeout(function() {
fn.apply(_that,args);
clearTimeout(timer)
},delay)
}
}
以上,如果有什么错误,欢迎指正。