TypeScript 防抖函数封装:原理与实现
防抖(Debounce)是前端开发中常用的性能优化技术,特别是在处理频繁触发的事件时非常有用。本文将介绍防抖的概念、原理,并通过 TypeScript 实现一个类型安全的防抖函数。
什么是防抖?
防抖是一种限制函数执行频率的技术。它的核心思想是:在一定时间内,无论触发多少次事件,都只执行最后一次。这在处理如窗口大小调整、输入框输入、滚动事件等高频触发场景时特别有用。
防抖的应用场景
- 搜索框输入联想(避免每次输入都发送请求)
- 窗口大小调整事件
- 滚动事件处理
- 按钮频繁点击防止重复提交
TypeScript 实现防抖函数
下面我们来实现一个类型安全的防抖函数:
代码语言:javascript代码运行次数:0运行复制/**
* 防抖函数封装
* @param fn 需要防抖的函数
* @param delay 防抖延迟时间(毫秒)
* @param immediate 是否立即执行
* @returns 包装后的防抖函数
*/
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number,
immediate: boolean = false
): (...args: Parameters<T>) => void {
let timer: ReturnType<typeof setTimeout> | null = null;
return function(this: ThisParameterType<T>, ...args: Parameters<T>) {
const context = this;
if (timer) {
clearTimeout(timer);
}
if (immediate && !timer) {
fn.apply(context, args);
}
timer = setTimeout(() => {
if (!immediate) {
fn.apply(context, args);
}
timer = null;
}, delay);
};
}
代码解析
- 泛型定义:使用泛型
T
来保持原始函数的类型签名,确保类型安全 - 参数类型:
fn: T
:要防抖的函数delay: number
:防抖延迟时间(毫秒)immediate: boolean
:是否立即执行(默认为 false)
- 内部变量:
timer
:存储 setTimeout 的返回值,用于清除定时器
- 返回函数:
- 保持原始函数的 this 上下文和参数类型
- 每次调用都会清除之前的定时器
- 根据
immediate
参数决定是立即执行还是延迟执行
使用示例
基本用法
代码语言:javascript代码运行次数:0运行复制// 普通函数
function search(query: string) {
console.log(`Searching for: ${query}`);
}
const debouncedSearch = debounce(search, 300);
// 快速连续调用
debouncedSearch("a");
debouncedSearch("ab");
debouncedSearch("abc");
// 最终只会输出: "Searching for: abc"
带立即执行的防抖
代码语言:javascript代码运行次数:0运行复制function submitForm(data: { name: string; age: number }) {
console.log("Submitting:", data);
}
const debouncedSubmit = debounce(submitForm, 500, true);
// 第一次调用会立即执行
debouncedSubmit({ name: "Alice", age: 25 });
// 快速连续调用
debouncedSubmit({ name: "Bob", age: 30 });
debouncedSubmit({ name: "Charlie", age: 35 });
// 最终会在延迟后执行最后一次
类方法中的使用
代码语言:javascript代码运行次数:0运行复制class SearchService {
private debouncedSearch: (query: string) => void;
constructor() {
this.debouncedSearch = debounce(this.search.bind(this), 300);
}
private search(query: string) {
console.log(`Performing search: ${query}`);
// 实际搜索逻辑...
}
public handleInput(query: string) {
this.debouncedSearch(query);
}
}
const service = new SearchService();
service.handleInput("t");
service.handleInput("ty");
service.handleInput("typ");
service.handleInput("types");
// 最终只会执行一次搜索: "Performing search: types"
高级改进:取消功能和返回值处理
我们可以进一步增强防抖函数,添加取消功能和 Promise 返回值支持:
代码语言:javascript代码运行次数:0运行复制interface DebouncedFunction<T extends (...args: any[]) => any> {
(...args: Parameters<T>): Promise<ReturnType<T>>;
cancel: () => void;
}
function advancedDebounce<T extends (...args: any[]) => any>(
fn: T,
delay: number,
immediate: boolean = false
): DebouncedFunction<T> {
let timer: ReturnType<typeof setTimeout> | null = null;
let resolveFn: ((value: ReturnType<T>) => void) | null = null;
const debounced = function(this: ThisParameterType<T>, ...args: Parameters<T>) {
const context = this;
return new Promise<ReturnType<T>>((resolve) => {
if (timer) {
clearTimeout(timer);
}
if (immediate && !timer) {
const result = fn.apply(context, args);
resolve(result);
}
timer = setTimeout(() => {
if (!immediate) {
const result = fn.apply(context, args);
resolve(result);
}
timer = null;
}, delay);
resolveFn = resolve;
});
} as DebouncedFunction<T>;
debounced.cancel = () => {
if (timer) {
clearTimeout(timer);
timer = null;
}
if (resolveFn) {
// 取消时 reject 可能会更合适,这里简化为 resolve undefined
resolveFn(undefined as ReturnType<T>);
resolveFn = null;
}
};
return debounced;
}
使用改进版
代码语言:javascript代码运行次数:0运行复制async function testAdvancedDebounce() {
const expensiveOperation = (value: number): number => {
console.log("Computing...", value);
return value * 2;
};
const debouncedOp = advancedDebounce(expensiveOperation, 200);
// 快速连续调用
const [res1, res2, res3] = await Promise.all([
debouncedOp(1),
debouncedOp(2),
debouncedOp(3)
]);
console.log(res1, res2, res3); // 6, 6, 6 (所有调用都返回最后一次的结果)
// 取消示例
const debouncedWithCancel = advancedDebounce(expensiveOperation, 500);
debouncedWithCancel(10).then(console.log); // 不会执行
debouncedWithCancel.cancel();
}
与节流(Throttle)的区别
防抖和节流都是控制函数执行频率的技术,但它们有不同的行为:
- 防抖:在事件停止触发后一段时间执行一次
- 节流:在一定时间间隔内最多执行一次
根据具体场景选择合适的方案:
- 防抖适合"最终状态"场景(如搜索建议)
- 节流适合"过程控制"场景(如滚动事件)
总结
本文介绍了:
- 防抖的概念和应用场景
- 使用 TypeScript 实现类型安全的防抖函数
- 如何扩展防抖函数的功能(取消、返回值处理)
- 防抖与节流的区别
通过 TypeScript 的泛型和类型系统,我们可以创建出既安全又灵活的防抖函数,为项目中的高频事件处理提供优雅的解决方案。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-28,如有侵权请联系 cloudcommunity@tencent 删除安全函数事件原理typescript