Lodash 防抖和节流有什么区别?各自适用什么场景?
防抖(debounce)和节流(throttle)都用于限制函数执行频率,但策略不同。防抖在事件停止触发后才执行——每次触发都重置计时器,所以连续触发期间函数不会执行,只在最后一次触发后的延迟时间到达时执行一次。节流则按固定时间间隔执行,不管事件触发多频繁,函数最多按间隔执行。核心区别:防抖关注"最后一次",节流关注"固定频率"。防抖适用于搜索框输入、窗口resize、表单验证——这些场景只关心最终状态。节流适用于滚动事件、鼠标移动、动画帧——这些场景需要持续响应但不能过于频繁。Lodash的_.debounce和_.throttle都支持leading(首次是否立即执行)和trailing(结束是否执行)选项,以及cancel()方法取消待执行的调用。
javascript// 防抖:每次触发重置计时器,只在停止后执行 function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // 节流:固定间隔执行,期间触发会被忽略或缓存 function throttle(func, wait) { let timeout, previous = 0; return function(...args) { const now = Date.now(); if (now - previous > wait) { func.apply(this, args); previous = now; } else { clearTimeout(timeout); timeout = setTimeout(() => { func.apply(this, args); previous = Date.now(); }, wait - (now - previous)); } }; }
Lodash用法示例:
javascriptimport _ from 'lodash'; // 防抖:搜索框300ms停止输入后才发请求 const debouncedSearch = _.debounce(keyword => fetchResults(keyword), 300); // 节流:滚动最多每100ms触发一次 const throttledScroll = _.throttle(() => checkLoadMore(), 100); window.addEventListener('scroll', throttledScroll); // leading/trailing选项 _.debounce(fn, 300, { leading: true, trailing: false }); // 首次立即执行 _.throttle(fn, 100, { leading: false, trailing: true }); // 首次不执行 // 组件卸载时取消 debouncedSearch.cancel(); throttledScroll.cancel();
图示对比:
shell防抖:触发 ●●●●●●●●●● → 执行 ● 节流:触发 ●●●●●●●●●● → 执行 ● ● ●
追问
leading和trailing选项怎么理解?
Lodash的防抖和节流都有leading和trailing两个布尔选项。leading: true表示延迟期开始时立即执行一次,trailing: true表示延迟期结束时再执行一次。默认值:防抖是leading: false, trailing: true(只在停止后执行),节流是leading: true, trailing: true(首尾各执行一次)。常见的配置:防抖搜索用{ leading: false, trailing: true }(默认);节流滚动用{ leading: true, trailing: true }(默认);按钮防重复点击可用{ leading: true, trailing: false }确保只执行首次。
防抖和节流在React中有什么坑?
React函数组件中每次渲染都会创建新的防抖/节流函数,导致无法正确缓存计时器。需要用useRef或useMemo保持同一个引用:
javascript// 错误:每次渲染创建新实例 const handleClick = _.debounce(fn, 300); // 无效 // 正确:useRef保持引用 const debouncedFn = useRef(_.debounce(fn, 300)).current; // 或用useMemo const debouncedFn = useMemo(() => _.debounce(fn, 300), []);
另外,组件卸载时要调用.cancel()清理待执行的定时器,否则可能对已卸载组件执行操作导致报错。
防抖的cancel和flush方法是做什么的?
cancel()取消待执行的延迟调用,清除计时器。场景:用户在防抖延迟期间主动提交表单,此时应该cancel()掉防抖,直接走提交逻辑。flush()立即执行当前待执行的延迟调用,如果当前没有待执行则什么都不做。场景:用户在防抖等待期间离开页面,可以用flush()确保最后一次输入被处理。两者都可在组件卸载时使用,cancel放弃执行,flush确保执行。
防抖能实现节流效果吗?
可以。防抖设置{ leading: true, trailing: true }加上maxWait选项就能近似节流效果。maxWait指定函数被延迟执行的最大时间,超过这个时间必定执行一次。_.throttle(fn, wait)实际上等价于_.debounce(fn, wait, { maxWait: wait })。反过来,节流无法实现防抖效果,因为节流无法做到"只在停止后执行"。