说到防抖和节流大家肯定都不陌生啦,今天来分享一个较为完善的防抖与节流代码,可选触发立即执行或delay之后再执行两种不同的情况。
1. 定义
先来回顾一下防抖和节流的定义:
- 防抖:触发事件n秒后执行,在n秒内重复触发,则重新计时
- 节流:n秒内只执行一次,在时间段内重复触发,不重复执行
like......⬇
2. 代码实现
- 防抖: 根据防抖的定义,这里可以用到定时器+闭包初步实现:
function debounce(fn, delay) {
let timer;
return function (...args) {
if (timer) clearTimeout(timer); // 判断 timer 是否为 null
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}}
可以看到,函数每次都会在delay后执行,如果我们需要首次触发事件或上一次执行delay秒后触发事件让函数立即执行,而不必先等delay秒呢? 需要传入immediate变量控制是否立即执行:
function debounce(fn, delay, immediate = false) {
let timer;
return function (...args) {
if (timer) clearTimeout(timer);
if (immediate) {
let isCall = !timer; // 如果 timer 为 null 或 undefined 则触发立即调用
if (isCall) fn.apply(this, args);
timer = setTimeout(() => {
timer = null;
}, delay)
} else {
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
}
- 节流:根据节流的定义,这里同样可以用到定时器+闭包初步实现:
function throttle(fn, delay) {
let timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay)
}
}
}
同样如果我们需要delay秒内让函数立即执行,而不必等delay秒呢?需要传入immediate变量控制是否立即执行,同时我们新增节流取消函数cancel:
function throttle(fn, delay, immediate = true) {
let timer = null;
return {
func: function (...args) {
if (!timer) {
if (immediate) {
fn.apply(this, args);
timer = setTimeout(() => {
timer = null
}, delay)
} else {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay)
}
}
},
cancel: function () {
clearTimeout(timer);
timer = null;
}
}
}
3. 应用场景
- 防抖
- 搜索框搜索输入,用户最后一个字符输入完特定延迟后,再发送请求
- 窗口大小resize,窗口调整完成后再计算窗口大小,防止重复渲染
- 节流
- 按钮快速点击事件,如多次快速点击表单提交按钮,防止频繁重复提交
- 鼠标移动事件监听,鼠标移动事件会高频触发,监听鼠标移动并实现实时绘制图形、动态更新元素样式等,可固定时间进行一次更新
- 窗口滚动事件监听,同理也会高频触发,如果在每次滚动事件触发时都去执行相应的业务逻辑(比如检查图片是否进入可视区域来懒加载),会对浏览器性能造成较大压力
end~