好的,阿杰 👍 我给你系统整理一篇 JavaScript 进阶篇——函数防抖(debounce)详解,包含概念、应用场景、原理代码和升级版本。
🚀 JavaScript 进阶篇 —— 函数防抖(debounce)
1. 什么是函数防抖?
函数防抖(debounce)是一种常见的 性能优化技巧:
在高频触发的事件中(如输入框输入、滚动、窗口大小调整),我们不希望函数被频繁调用,而是 只在最后一次操作结束后的一段时间内执行。
👉 核心思想:你一直触发我就不执行,等你不动了再执行一次。
2. 应用场景
- 输入搜索框:用户输入时不断触发
input
事件,但只在用户停止输入后再发起请求。 - 窗口 resize:调整浏览器大小时触发很多次
resize
,只在调整结束后重新计算布局。 - 按钮点击防重复:防止用户在短时间内多次点击按钮,避免重复提交。
3. 基础实现
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 使用示例:搜索框输入
const input = document.querySelector("#search");
input.addEventListener("input", debounce(function(e) {
console.log("搜索内容:", e.target.value);
}, 500));
解释:
- 每次触发事件,先清除定时器
clearTimeout(timer)
。 - 重新开启定时器,等
delay
毫秒后执行函数。 - 如果用户持续输入,定时器就一直被清掉,直到停下来。
4. 立即执行版(leading)
有时我们希望函数在第一次触发时立即执行,然后等用户停下来不再触发。
function debounce(fn, delay, immediate = false) {
let timer = null;
return function(...args) {
if (timer) clearTimeout(timer);
if (immediate && !timer) {
fn.apply(this, args);
}
timer = setTimeout(() => {
timer = null;
if (!immediate) fn.apply(this, args);
}, delay);
};
}
immediate = true
→ 先执行一次,再防抖。immediate = false
→ 延迟执行(常见场景)。
5. 带返回值的防抖
有时候我们需要 拿到函数的返回值,就要用 Promise
封装:
function debounce(fn, delay, immediate = false) {
let timer = null;
return function(...args) {
return new Promise((resolve) => {
if (timer) clearTimeout(timer);
if (immediate && !timer) {
resolve(fn.apply(this, args));
}
timer = setTimeout(() => {
timer = null;
if (!immediate) resolve(fn.apply(this, args));
}, delay);
});
};
}
6. 防抖 vs 节流(throttle)
⚡ 防抖(debounce):事件触发后 只执行最后一次。
⚡ 节流(throttle):事件触发后 按固定时间间隔执行。
📌 对比例子:
- 防抖:电梯按钮,只有最后一次按下才有效。
- 节流:公交车,每隔 5 分钟发车一次,不管有多少人等。
✅ 总结
- 防抖核心:
clearTimeout + setTimeout
。 - 适合 输入框、resize、滚动停止 等场景。
- 可扩展:支持立即执行、返回值、Promise 封装。
好👌 阿杰,我帮你整理一个 可复用的防抖(debounce)+ 节流(throttle)工具库,保存成 utils.js
就能在项目里随时用。
📦 utils.js
/**
* 函数防抖 debounce
* @param {Function} fn - 需要防抖的函数
* @param {number} delay - 延迟时间(ms)
* @param {boolean} immediate - 是否立即执行
* @returns {Function}
*/
export function debounce(fn, delay = 300, immediate = false) {
let timer = null;
return function (...args) {
const context = this;
if (timer) clearTimeout(timer);
if (immediate && !timer) {
fn.apply(context, args);
}
timer = setTimeout(() => {
timer = null;
if (!immediate) fn.apply(context, args);
}, delay);
};
}
/**
* 函数节流 throttle
* @param {Function} fn - 需要节流的函数
* @param {number} delay - 时间间隔(ms)
* @param {boolean} leading - 是否立即执行一次
* @param {boolean} trailing - 是否在最后执行一次
* @returns {Function}
*/
export function throttle(fn, delay = 300, leading = true, trailing = true) {
let lastCall = 0;
let timer = null;
return function (...args) {
const context = this;
const now = Date.now();
if (!lastCall && !leading) lastCall = now;
const remaining = delay - (now - lastCall);
if (remaining <= 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
lastCall = now;
fn.apply(context, args);
} else if (!timer && trailing) {
timer = setTimeout(() => {
lastCall = leading ? Date.now() : 0;
timer = null;
fn.apply(context, args);
}, remaining);
}
};
}
🔧 使用方法
1. 防抖(输入框搜索)
import { debounce } from './utils.js';
const input = document.querySelector('#search');
input.addEventListener('input', debounce((e) => {
console.log('搜索:', e.target.value);
}, 500));
👉 用户停止输入 500ms 后才触发。
2. 节流(页面滚动)
import { throttle } from './utils.js';
window.addEventListener('scroll', throttle(() => {
console.log('滚动位置:', window.scrollY);
}, 1000));
👉 每隔 1 秒最多触发一次,避免频繁触发。
3. 点击按钮防止连点
import { debounce } from './utils.js';
document.querySelector('#btn').addEventListener('click', debounce(() => {
console.log('只执行一次提交');
}, 1000, true)); // 立即执行版
✨ 这样以后你直接 import { debounce, throttle }
就能用,方便在各种项目里复用。
发表回复