防抖(debounce)
如果在N秒内高频执行某个操作会触发一个函数,我们希望在N秒后只执行一次该函数。N秒未到时又执行这个操作,从零开始计时直到N秒结束再执行该函数。
实际场景经常出现在输入框输入文字时触发的回调函数,以此为例:
<input type="text" oninput="change(this)" />
function change(obj){
console.log(obj.value)
}
输入文字时,输入框内的字符每改变一次就实时调用change方法,如果用户连续快速输入,会多次调用目标函数。这种体验当然不好,如果调用的方法里还包含调用了接口,后果可想而知。
关于函数防抖(debounce)的一种简单实现。如下面的代码:
const debounce = (fn, wait) => {
let timer = null;
return function(){
clearTimeout(timer);
timer = setTimeout(()=>{
fn.apply(this, arguments);
}, wait);
};
};
注意: fn.apply(this, arguments);
这行代码是把debounce
实现中 return
的整体function(){}
的参数转移到我们最终要调用的函数fn
中。这行代码很关键,它能保证我们通过使用函数防抖debounce
包裹的目标函数fn
被调用时能正常传参。
使用函数防抖:
<input type="text" oninput="change(this)" />
const change = debounce(function(obj){
console.log(obj.value)
},1000);
html
部分没变,注意 change
方法的定义,因为防抖函数体 debounce
的实现本身也是闭包的形态,闭包的目的是要把 timer
变量私有化,用来判断时间间隔。
最终调用还是 change(this)
这样,其实它调用了已经被缓存(或者说是被预加载)过的 change
方法。至于 this
的传参,前面说过是 fn.apply(this, arguments);
的功劳。
节流(throttle)
节流也是在N秒内高频执行某个操作,结果是按传入的毫秒数作为时间间隔,连续调用目标函数。
还是要以真实场景为例,比如窗体滚动事件:
<style>
.father{width:300px;height:300px;overflow:hidden scroll;}
.child{width:300px;height:2500px;background-color: goldenrod;}
</style>
<div class="father" onscroll="normalScroll(this)">
<div class="child"></div>
</div>
<script>
const normalScroll = el => {
console.log(el);
}
</script>
以上是普通滚动事件
下面是节流函数调用滚动事件:
<style>
.father{width:300px;height:300px;overflow:hidden scroll;}
.child{width:300px;height:2500px;background-color: goldenrod;}
</style>
<div class="father" onscroll="throttleScroll(this)">
<div class="child"></div>
</div>
<script>
// 函数节流实现
const throttle = (fn, wait) => {
let flag = true;
return function() {
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, arguments);
flag = true;
}, wait);
}
}
// 定义函数节流滚动事件
const throttleScroll = throttle(el => {
console.log(el);
},1000);
</script>
与函数防抖用法相同,也是先定义一个预加载函数 throttleScroll
,然后二次调用传参。
最后说说使用场景,
关于防抖,我最近常用的是在输入框输入搜索关键字后调用防抖函数(接口)。其实还有鼠标快速移动经过某个按钮或选项卡,或页面窗口缩放事件等使用场景。
节流 上面的例子是页面滚动或局部 div
滚动调用函数。节流区别于防抖是是否持续调用目标函数,防抖只是调用一次目标函数。只要需求满足需要持续调用,我们就用节流。