面试题库 词条目录
前端Wiki/面试题库/JavaScript/手写代码题

手写节流与防抖

防抖(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 / 时间戳是手写核心。