在前端开发中,我们经常会遇到一些高频事件,比如窗口的
resize
、scroll
、输入框的input
等。如果我们对这些事件的回调函数进行频繁的执行,可能会导致页面卡顿、性能下降、甚至内存泄漏。为了解决这个问题,我们可以使用两种常见的技巧:防抖和节流。本文将从概念理解、应用场景、区别、手写实现等多个角度来介绍防抖和节流的原理和用法。
防抖的英文是debounce
,它的含义是在一定时间内,只执行最后一次触发的事件。也就是说,如果一个事件被连续触发,那么只有当它停止触发一段时间后,才会执行回调函数。这样可以避免对同一个事件进行重复的处理。
举个例子,假设我们有一个搜索框,当用户输入关键词后,会自动发送请求给后端,返回搜索结果。
如果我们不对输入事件进行防抖处理,那么每输入一个字符,就会发送一个请求,这样会造成大量的无效请求和资源浪费。
如果我们对输入事件进行防抖处理,那么只有当用户停止输入一段时间后(比如500ms
),才会发送最后一次输入的请求,这样可以减少请求数量和提高用户体验。
防抖的实现思路很简单,就是利用定时器。
当事件触发时,我们设置一个定时器,并记录下当前的时间戳。如果在定时器执行之前,又触发了同一个事件,那么我们就清除之前的定时器,并重新设置一个定时器。这样就保证了只有最后一次触发的事件才会执行回调函数。
下面是一个简单的防抖函数的实现:
js// debounce函数接收一个函数fn和一个延迟时间delay作为参数
function debounce(fn, delay) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 使用示例
// 假设有一个input元素和一个search函数
let input = document.getElementById("input");
let search = function() {
console.log("searching...");
};
// 对input元素的input事件进行防抖处理,延迟500ms执行search函数
input.addEventListener("input", debounce(search, 500));
上面我们实现的防抖函数,是一个很常规、基础版的实现,在有些面试中,可能基于各种场景来做,我遇到比较多的就是,让第一次触发正常执行,之后的触发再进行防抖。
jsfunction debouncePlus(fn, delay) {
let timer = null;
let first = true;
return function (...args) {
if (timer) clearTimeout(timer);
if (first) {
fn.apply(this, args);
first = false;
} else {
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
}
};
}
其实也很简单,多声明一个变量来判断状态即可。
节流的英文是throttle
,它的含义是在一定时间内,只执行第一次触发的事件。也就是说,如果一个事件被连续触发,那么在规定的时间间隔内只会执行一次回调函数。这样可以保证对高频事件有一定的控制。
举个例子,假设我们有一个滚动条,当用户滚动页面时,会触发scroll
事件,并根据滚动位置显示或隐藏一些元素。
如果我们不对scroll
事件进行节流处理,那么每滚动一点距离,就会触发一次回调函数,这样会导致页面抖动、性能下降、甚至浏览器崩溃。
如果我们对scroll
事件进行节流处理,那么只有当滚动的距离达到一定的阈值后,才会执行一次回调函数,这样可以提高页面的流畅度和稳定性。
节流的实现方式不唯一,思路也很简单,可以利用定时器,也可以利用时间戳。
定时器方式实现节流函数的思路是,在事件触发时先判断定时器是否存在,如果不存在则创建一个定时器,在一定时间间隔后执行回调函数并清除定时器。如果定时器已存在,则不执行回调函数,直接返回。
时间戳方式实现节流函数的思路是,在事件触发时先记录当前时间戳,然后再判断当前时间戳与上次执行回调函数的时间戳的时间间隔是否超过限制时间间隔,如果超过则执行回调函数并更新上次执行回调函数的时间戳。如果未超过,则不执行回调函数,直接返回。
下面是分别使用定时器和时间戳实现一个简单的节流函数:
js// 定时器方式
function throttle(fn, delay) {
let timer = null;
return function() {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
};
}
// 时间戳方式
function throttle(fn, delay) {
let lastTime = 0;
return function() {
const nowTime = Date.now();
if (nowTime - lastTime > delay) {
fn.apply(this, arguments);
lastTime = nowTime;
}
};
}
// 使用示例
// 假设有一个div元素和一个showOrHide函数
let div = document.getElementById("div");
let showOrHide = function() {
console.log("showing or hiding...");
};
// 对div元素的scroll事件进行节流处理,每1000ms执行一次showOrHide函数
div.addEventListener("scroll", throttle(showOrHide, 1000
防抖和节流都是为了优化高频事件的处理,但它们有一些区别:
resize
等。节流适用于连续触发但需要按照一定频率执行的场景,比如滚动条的滚动、鼠标的拖拽等。防抖和节流是两种常用的前端性能优化技巧,它们可以有效地控制高频事件的处理。
防抖是在一定时间内只执行最后一次触发的事件,节流是在一定时间内只执行第一次触发的事件。它们适用于不同的场景,需要根据具体需求来选择合适的方案。
本文介绍了防抖和节流的概念理解、应用场景、区别、手写实现等多个角度,希望对你有所帮助。
本文作者:CreatorRay
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!