JS实现节流函数的核心是通过控制函数执行频率,确保在指定时间间隔内最多执行一次,常见方法包括时间戳方式和定时器方式。
- 时间戳方式通过比较当前时间与上次执行时间的差值是否超过设定延迟来决定是否执行函数,首次触发立即执行。示例代码:
function throttleByTimestamp(func, delay) { let lastTime = 0; return function(...args) { const now = Date.now(); if (now - lastTime >= delay) { func.apply(this, args); lastTime = now; } };}// 使用示例function handleScroll() { console.log('Scroll event triggered');}const throttledScrollHandler = throttleByTimestamp(handleScroll, 200);window.addEventListener('scroll', throttledScrollHandler);- 定时器方式通过设置定时器,在延迟期间内禁止重复触发,延迟结束后执行函数。示例代码:
function throttleByTimeout(func, delay) { let timeoutId = null; return function(...args) { if (!timeoutId) { timeoutId = setTimeout(() => { func.apply(this, args); timeoutId = null; }, delay); } };}// 使用示例function handleResize() { console.log('Resize event triggered');}const throttledResizeHandler = throttleByTimeout(handleResize, 300);window.addEventListener('resize', throttledResizeHandler);两种方式的区别
- 执行时机:时间戳方式首次触发立即执行,后续按延迟时间间隔执行;定时器方式首次触发后需等待延迟时间结束才执行,期间重复触发无效。
- 适用场景:时间戳方式适合需要立即响应的场景(如滚动事件实时反馈);定时器方式适合可延迟执行的场景(如避免频繁请求数据)。
节流与防抖的区别
- 节流:限制函数在一段时间内最多执行一次,适用于持续高频触发但需限制频率的场景(如scroll、resize事件)。
- 防抖:等待一段时间后执行,若期间再次触发则重新计时,适用于只需最后一次操作后执行的场景(如搜索输入、表单验证)。防抖示例代码:
function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(this, args); }, delay); };}// 使用示例function search(query) { console.log(`Searching for: ${query}`);}const debouncedSearch = debounce(search, 500);const searchInput = document.getElementById('search-input');searchInput.addEventListener('input', (e) => { debouncedSearch(e.target.value);});性能优化建议
- 避免内部创建函数:减少节流函数内部闭包或匿名函数的创建,可降低内存占用。
- 使用requestAnimationFrame替代setTimeout:适合动画相关节流,与浏览器重绘同步,避免卡顿。示例代码:
function throttleByAnimationFrame(func) { let isRunning = false; return function(...args) { if (!isRunning) { isRunning = true; requestAnimationFrame(() => { func.apply(this, args); isRunning = false; }); } };}// 使用示例function updatePosition() { console.log('Updating position');}const throttledUpdatePosition = throttleByAnimationFrame(updatePosition);window.addEventListener('scroll', throttledUpdatePosition);- 合理设置延迟时间:根据实际需求调整delay,避免过短导致频繁执行或过长导致响应延迟。
- 减少DOM操作:合并多次DOM操作为一次执行,降低重绘和重排开销。
其他实现方法
const throttledFunction = _.throttle(() => { console.log('Lodash throttled function');}, 500);window.addEventListener('scroll', throttledFunction);- RxJS:通过throttleTime操作符实现节流,适合响应式编程场景。
- 自定义实现:结合闭包、位运算等优化性能,但需权衡可读性与复杂度。
选择建议
- 项目需求:若需快速实现且稳定性优先,推荐使用Lodash等成熟库;若需灵活控制逻辑,可自定义实现。
- 场景匹配:根据事件类型选择节流或防抖,高频持续触发用节流,末次触发执行用防抖。
- 性能考量:对动画或视觉反馈要求高的场景,优先使用requestAnimationFrame优化。