903 字
5 分钟
JavaScript 防抖

题目描述#

实现 JavaScript 防抖函数,要求:

  1. 延迟执行函数直到停止触发后的指定时间
  2. 支持立即执行选项(首次触发立即执行)
  3. 支持取消功能(在等待期间取消执行)
  4. 支持返回值处理(适用于 Promise 场景)
  5. 正确处理函数参数和 this 绑定

要求完整实现防抖逻辑,并通过测试用例验证。

解题思路#

  1. 计时器管理:使用 setTimeout 实现延迟执行
  2. 立即执行选项:首次触发立即执行函数
  3. 取消机制:提供取消执行的方法
  4. 参数传递:保存函数参数并正确传递
  5. 返回值处理:支持 Promise 返回值
  6. this 绑定:确保函数执行上下文正确

关键洞察#

  1. 延迟执行本质:通过清除重置计时器实现延迟效果
  2. 立即执行分离:首次调用立即执行,后续调用防抖
  3. 参数保存机制:闭包保存参数确保最新值
  4. Promise 支持:通过返回值封装 Promise
  5. 内存管理:及时清除计时器避免内存泄漏
  6. 上下文绑定:使用 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

业务场景#

  1. 搜索联想:用户输入停止后才触发搜索请求
  2. 窗口调整:窗口停止调整后执行布局计算
  3. 滚动事件:滚动停止后执行检查(如无限滚动)
  4. 按钮防重:防止表单重复提交
  5. 实时保存:内容编辑停止后自动保存
  6. Canvas绘制:窗口调整后重绘Canvas元素

性能优势

  • 减少高频率触发事件的处理次数
  • 避免不必要的函数执行开销
  • 优化前端性能,减少浏览器压力
  • 防止接口频繁请求,减轻服务器负担

相似题目#

  1. 节流(Throttle)实现(中等) 核心:固定时间间隔内只执行一次
  2. 请求取消实现(中等) 核心:AbortController + fetch 中断请求
  3. 动画帧节流(中等) 核心:requestAnimationFrame 实现动画控制
  4. Promise 并发控制(困难) 核心:Promise 队列管理并发数量
  5. 滚动位置监听优化(中等) 核心:IntersectionObserver + 节流
JavaScript 防抖
https://website-truelovings-projects.vercel.app/posts/code/javascript/javascript-防抖/
作者
欢迎来到StarSky的网站!
发布于
2024-08-17
许可协议
CC BY-NC-SA 4.0