函数防抖&函数节流
函数防抖:当调用动作过N毫秒后,才会执行该动作,若在这N毫秒又调用次动作则将重新计算执行时间
函数节流:预选设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期
函数防抖和函数节流都是为了限制的执行次数,以优化函数触发频率过高而导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。
比如下面情况:
- window对象的resize scroll事件
- 拖拽时的mousemove 事件
- 文字输入,自动完成的keyup事件
可以拿乘电梯来形象的表述二者的区别
函数防抖: 如果有人进地电梯(触发事件),那电梯将在10秒钟后出发(执行事件监听器),这是如果又有人进电梯了(10s内再次触发了事件),我们又得等待10s
函数节流: 设定如果有人进入了电梯,10s后准时运送一次,这个时间是从第一个人进入电梯开始计时,不等待,如果没有人就不运行
实现
(debounce)函数防抖
function _debounce(fn, wait) {
var timer = null;
return function() {
clearTimeout(timer)
timer = setTimeout(() => {
fn();
}, wait)
}
}
function _log() {
console.log(1)
}
window.scroll = _debounce(_log, 500)
上面的实现方式还是有一定的缺点,如果页面很长,我们一直在滚动页面,那么_log方法就一直不会被执行,所以我们可以升级一下函数防抖方法
function _debounce(fn, wait, time) {
var pervious = null; // 记录上一次运行的时间
var timer = null;
return function() {
var now = +new Date();
if(!perivous) perivous = now;
// 当上一次的执行时间与当前的时间差大于所设置的执行时间间隔时长的话,就主动执行一次
if(perivous - now > time) {
clearTimeOut(timer);
fn();
perivous = now; // 执行完了之后马上记录当前时间
} else {
clearTimeout(timer);
timer = setTimeout(() => {
fn();
}, wiat);
}
}
}
function _log() {
console.log(1);
}
window.scroll = _debounce(_log, 200, 5000);
函数防抖的应用场景,就是对于连续的事件响应我们只执行一次回调
- 每次resize/scroll 触发统计事件
- 文本输入验证(连续输入文字后发送ajax请求验证,验证一次就好)
(throttle)函数节流
function _throttle(fn, time) {
let self = fn,
timer = null,
firstTime = true; // 记录是否是第一次执行的flag
return function() {
let args = arguments, // 解决闭包传参问题
_me = this; // 解决上下文丢失问题
if(firstTime) { // 若是第一次,则直接执行
_self.apply(_me, args);
return fristTime = false;
}
if(timer) { // 定时器存在, 说明有事件监听器在执行,直接返回
return false;
}
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
_self.apply(_me, args);
}, time || 500)
}
}
函数节流的应用场景:
- Dom元素的拖拽功能实现(mousemove)
- 射击游戏的mousedown/keydown事件(单位时间内只能发射一颗子弹)
- 计算鼠标移动的距离(mousemove)
- Canvas模拟画板功能(mousemove)
- 搜索联想(keyup)