903 字
5 分钟
JavaScript 防抖

题目描述
实现 JavaScript 防抖函数,要求:
- 延迟执行函数直到停止触发后的指定时间
- 支持立即执行选项(首次触发立即执行)
- 支持取消功能(在等待期间取消执行)
- 支持返回值处理(适用于 Promise 场景)
- 正确处理函数参数和 this 绑定
要求完整实现防抖逻辑,并通过测试用例验证。
解题思路
- 计时器管理:使用 setTimeout 实现延迟执行
- 立即执行选项:首次触发立即执行函数
- 取消机制:提供取消执行的方法
- 参数传递:保存函数参数并正确传递
- 返回值处理:支持 Promise 返回值
- this 绑定:确保函数执行上下文正确
关键洞察
- 延迟执行本质:通过清除重置计时器实现延迟效果
- 立即执行分离:首次调用立即执行,后续调用防抖
- 参数保存机制:闭包保存参数确保最新值
- Promise 支持:通过返回值封装 Promise
- 内存管理:及时清除计时器避免内存泄漏
- 上下文绑定:使用 apply 确保 this 正确
代码流程
flowchart TD A[调用防抖函数] --> B{立即执行?} B -->|是| C[立即执行函数] B -->|否| D[清除旧计时器] D --> E[设置新计时器] E -->|等待延迟| F[执行函数] G[取消操作] --> H[清除计时器]
代码实现
function debounce(func, wait = 300, options = {}) { let timerId = null; let lastArgs = null; let lastThis = null; let result = null;
// 立即执行选项默认值 const leading = !!options.leading; const trailing = 'trailing' in options ? !!options.trailing : true;
// 主函数 function debounced(...args) { const now = Date.now(); lastArgs = args; lastThis = this;
// 立即执行分支 if (leading && !timerId) { result = invokeFunc(now); }
// 清除旧计时器 clearTimeout(timerId);
// 设置新计时器 timerId = setTimeout(() => { // 非立即执行模式调用 if (trailing && lastArgs) { result = invokeFunc(Date.now()); }
// 清理状态 timerId = null; lastArgs = null; lastThis = null; }, wait);
return result; }
// 执行目标函数 function invokeFunc(time) { return func.apply(lastThis, lastArgs); }
// 取消方法 debounced.cancel = function() { clearTimeout(timerId); timerId = null; lastArgs = null; lastThis = null; };
// 立即执行当前值 debounced.flush = function() { clearTimeout(timerId); return invokeFunc(Date.now()); };
return debounced;}
测试代码
// 基础防抖const basicDebounce = debounce(() => { console.log('Resize event handled');}, 250);
window.addEventListener('resize', basicDebounce);
// 立即执行防抖const searchDebounce = debounce((query) => { console.log(`Searching for: ${query}`);}, 500, {leading: true});
searchInput.addEventListener('input', (e) => { searchDebounce(e.target.value);});
// 取消示例const cancelableDebounce = debounce(() => { console.log('This will not run if canceled');}, 1000);
cancelableDebounce();setTimeout(cancelableDebounce.cancel, 500); // 取消执行
// Promise支持const promiseDebounce = debounce((value) => { return Promise.resolve(`Processed: ${value}`);}, 300);
promiseDebounce('data').then(console.log); // 输出: Processed: data
业务场景
- 搜索联想:用户输入停止后才触发搜索请求
- 窗口调整:窗口停止调整后执行布局计算
- 滚动事件:滚动停止后执行检查(如无限滚动)
- 按钮防重:防止表单重复提交
- 实时保存:内容编辑停止后自动保存
- Canvas绘制:窗口调整后重绘Canvas元素
性能优势:
- 减少高频率触发事件的处理次数
- 避免不必要的函数执行开销
- 优化前端性能,减少浏览器压力
- 防止接口频繁请求,减轻服务器负担
相似题目
- 节流(Throttle)实现(中等) 核心:固定时间间隔内只执行一次
- 请求取消实现(中等) 核心:AbortController + fetch 中断请求
- 动画帧节流(中等) 核心:requestAnimationFrame 实现动画控制
- Promise 并发控制(困难) 核心:Promise 队列管理并发数量
- 滚动位置监听优化(中等) 核心:IntersectionObserver + 节流