定义
- 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
- 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
一个经典的比喻:
想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应
假设电梯有两种运行策略 debounce 和 throttle,超时设定为15秒,不考虑容量限制
电梯第一个人进来后,15秒后准时运送一次,这是节流
电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖
节流代码实现
时间戳方法
function throttled(fn, delay = 1000) {
let starttime = Date.now()
return function (...args) {
let endtime = Date.now();
if (endtime - starttime >= delay) {
fn.apply(this, args);
starttime = Date.now();
}
}
}
缺点:可能会造成最后一次事件不会被触发
计时器方法
function throttled(fn, delay = 1000) {
let timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
}
}
}
缺点:第一次事件不会立即执行
时间戳与计时器混合使用
function throttled(fn, delay = 1000) {
let timer = null;
let starttime = Date.now();
return function (...args) {
let endtime = Date.now();
clearTimeout(timer);
if (endtime - starttime >= delay) {
fn.apply(this, args);
starttime = endtime;
} else {
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
}
}
}
防抖代码实现
非立即执行
function debounce(fn, wait) {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, args);
}, wait);
}
}
立即执行
function debounce(fn, wait) {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
}
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) fn.apply(this, args);
}
}