防抖是什么
防抖就是对于频繁触发的事件设定一个最小触发间隔,如果触发间隔小于设定的间隔,则清除原来的定时,重新设定新的定时;如果触发间隔大于设定间隔,则保留原来的定时,并设置新的定时;防抖的结果就是一段时间内频繁的触发转变为一次触发
哪些场景需要防抖
在频繁触发事件的场景,有些情况可能执行的逻辑比较复杂或者耗时,此时浏览器的处理跟不上触发,就会发生卡顿、假死或者事件堆积,这里防抖就可以一定程度上解决或者缓解这种故障。
常见的需要防抖的场景: 搜索框keyup、keydown等触发后台请求; 频繁改变窗口大小resize;类似以上频繁触发但是通常只在乎最终结果的情况;
防抖实现
基础实现
const debounce = (fn, wait) => {
let timer;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
}, wait);
}
};
如果想让事件立即触发一次呢?
const debounce = (fn, wait, immediate = false) => {
let timer;
return function() {
if(timer) clearTimeout(timer);
if(immediate) {
let trigger = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if(trigger) {
fn.apply(this, arguments);
}
return;
}
timer = setTimeout(() => {
fn.apply(this, arguments);
}, wait);
}
};
如果这个防抖时间比较长,希望可以取消这种“挂起”状态呢?
const debounce = (fn, wait, immediate = false) => {
let timer;
const debounced =function(){
if(timer) clearTimeout(timer);
if(immediate) {
let trigger = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if(trigger) {
fn.apply(this, arguments);
}
return;
}
timer = setTimeout(() => {
fn.apply(this, arguments);
}, wait);
return;
}
debounced.cancel = () => {
clearTimeout(timer);
timer = null;
}
return debounced;
};
节流是什么
节流就是持续触发的事件,每隔一段时间触发一次。
哪些场景需要节流?
在频繁触发事件的场景,有些情况可能执行的逻辑比较复杂或者耗时,此时浏览器的处理跟不上触发,就会发生卡顿、假死或者事件堆积,为了解决这个故障,节流是其中之一的策略
常见的需要防抖的场景: 页面滚动事件监听;mousemove事件触发;类似以上频繁触发但是无需每次触发都需要结果的场景。
实现节流
版本1,设定间隔,如果当前请求触发时间和上次触发间隔小于设定间隔,则不触发
const throttle = (fn, wait) => {
let pre = 0, context, args;
return function() {
context = this;
args = arguments;
const now = new Date().getTime();
if(now - pre > wait) {
fn.apply(context, args);
pre = now;
}
}
};
版本2,使用定时器;首次触发设定新的定时器,如果间隔小于预设,则清除旧的定时器;
const throttle = (fn, wait) => {
let context, args, timer;
const run = () => {
timer = setTimeout(() => {
fn.apply(context, args);
clearTimeout(timer);
timer = null
}, wait);
}
return function() {
context = this;
args = arguments;
if(!timer) {
run();
}
}
};
版本3 首次触发事件时立即执行一次,在事件触发结束后再执行一次
const throttle = (fn, wait) => {
let pre = 0, context, args, timer;
const run = () => {
pre = Date.now();
fn.apply(context, args);
clearTimeout(timer);
timer = null;
}
return function() {
context = this;
args = arguments;
const now = Date.now();
const remain = wait - (now - pre);
if(Math.abs(remain) > wait || remain < 0) {
pre = Date.now();
fn.apply(context, args);
} else if(!timer){
timer = setTimeout(run, wait);
}
}
};