手写节流与防抖
防抖(debounce):事件停止触发 n 秒后才执行,期间再来就重新计时 —— 适用于搜索框输入、窗口 resize。节流(throttle):n 秒内最多执行一次,稀释执行频率 —— 适用于滚动加载、按钮防连点。两者都在“高性能滚动 / 输入”场景出场,区别可以记成“防抖重置计时器、节流锁阀门”。
手写要点:用闭包持有 timer / 时间戳;debounce 在每次调用清掉旧定时器;throttle 用上一次执行时间戳判断是否超过间隔。进阶版带 immediate(首次立即执行)与 cancel(组件卸载时清理),面试写出这些就是加分项。
- 防抖 = 重置计时器:停了再执行,场景:搜索输入、resize
- 节流 = 间隔内只执行一次,场景:滚动加载、按钮防连点
- 手写核心:闭包持有 timer / 时间戳;进阶带 immediate 与 cancel
JS
// 手写防抖:停了再执行 function debounce(fn, wait) { let timer = null; return function (...args) { clearTimeout(timer); // 再来就重新计时 timer = setTimeout(() => fn.apply(this, args), wait); }; } // 手写节流:间隔内只执行一次 function throttle(fn, wait) { let last = 0; return function (...args) { const now = Date.now(); if (now - last >= wait) { // 超过间隔才放行 last = now; fn.apply(this, args); } }; } // 验证:模拟连续触发 const log = (tag) => console.log(tag, '执行于', new Date().toLocaleTimeString()); const d = debounce(() => log('防抖'), 300); const t = throttle(() => log('节流'), 300); // 快速连点 5 次(约每 100ms 一次) let n = 0; const id = setInterval(() => { d(); t(); if (++n === 5) clearInterval(id); }, 100); // 结果:节流约执行 1~2 次,防抖只在停止 300ms 后执行 1 次 // 带立即执行与取消的完整版 function debounceFull(fn, wait, immediate = false) { let timer = null; const debounced = function (...args) { clearTimeout(timer); if (immediate && !timer) fn.apply(this, args); // 首次立即执行 timer = setTimeout(() => { timer = null; if (!immediate) fn.apply(this, args); }, wait); }; debounced.cancel = () => { clearTimeout(timer); timer = null; }; return debounced; }
本节要点防抖重置计时器(停才执行),节流按间隔放行(稀释频率);闭包持有 timer / 时间戳是手写核心。




