节流 (throttle) 在做什么?
节流 (throttle) 指的是,在一段时间内只会执行一次触发事件的回调 (callback) 函数,若在这之中又有新事件触发,则不执行此回调函数。
在手写节流 (throttle) 函数前,我们先来了解节流的使用场景。
监听滚动事件,是搭配节流的使用场景之一。举例来说,要判断使用者是否已经滑动到页面的 30% 处,当到达时会触发一些动画效果,因此,会通过监听滚动事件时计算是否已到达该位置,但如果只要一滚动就计算会非常消耗性能,通过节流 (throttle) 可以将计算的这个回调函数在固定时间内合并一次被执行。
这里不适合使用防抖的原因是,防抖只会在事件停止被触发后的一段时间内被执行一次。因此如果用防抖,当使用者一直滑动页面,函数就永远不会被触发。这边我们仍想要函数在滑动过程中被触发,只是不想那么频繁被触发,这种场景下,节流就可以派上用场。
节流函数会接受两个参数
- 延迟的时间 (ms)
- 要执行的回调 (callback) 函数
手写节流 (throttle) 函数
我们先直接看代码,看看你能了解多少。有不懂的地方也不担心,下面会通过注解,一行行解释:
function throttle(fn, delay = 500) {
let timer = null;
return (...args) => {
if (timer) return;
timer = setTimeout(() => {
fn(...args);
timer = null;
}, delay);
};
}
代码注解版本和实际应用
function throttle(fn, delay = 500) {
let timer = null;
// throttle 本身会返回一个函数,通过 ...args 拿到该函数的变量
return function (...args) {
// 如果有计时器,表示还在 delay 的秒数内
// 直接 return,不往下执行代码
if (timer) return;
// 如果计时器不等于 null,会进到以下逻辑
// 设置计时器,在 delay 秒数之后,将计时器值为改为 null
// 如果还不到 delay 的秒数,再执行一次的话
// 因为 timer 的值不为 null,前面就先 return 不会进到这段逻辑
// 可以达到 throttle 的目的,将一段时间内的操作,集合成一次执行
timer = setTimeout(() => {
timer = null;
}, delay);
// 直到时间到了后,timer 变成 null,才能够执行函数
// 用 .apply 来调用 fn
fn.apply(this, args);
};
}
const updateThrottleText = throttle(() => {
console.log("throttle");
}, 500);
// 如果一直滑动页面,会固定 500ms console.log('throttle')
window.addEventListener("scroll", () => {
updateThrottleText();
});