FE Interview Hub
Web Vitals中階

什麼是防抖 (debounce)?如何實踐防抖 (debounce) 函式?

AI 練習作答

什麼是 Debounce(防抖)?

防抖:當事件被高頻觸發時,只有在「最後一次觸發後的 N 毫秒內沒有再被觸發」,才真正執行函式。如果在等待期間事件又被觸發,就重新計時。

核心精神:只在「停下來」之後才執行

常見應用情境

  • 搜尋框輸入自動補全(使用者停止輸入後才發請求)
  • 視窗 resize 後才重算版面
  • 表單欄位即時驗證
  • 按鈕防止連續快點(送出表單)

基本實作

function debounce(fn, delay = 300) {
  let timer = null;

  return function (...args) {
    // 每次觸發先清掉上一次的計時器
    if (timer) clearTimeout(timer);

    timer = setTimeout(() => {
      fn.apply(this, args);
      timer = null;
    }, delay);
  };
}

使用:

const onInput = debounce((e) => {
  console.log('search:', e.target.value);
}, 500);

input.addEventListener('input', onInput);

進階版:支援 leading / trailing 與 cancel

  • leading: 第一次觸發時立刻執行
  • trailing: 最後一次觸發後執行(預設)
  • cancel(): 取消尚未執行的 callback
function debounce(fn, delay = 300, { leading = false, trailing = true } = {}) {
  let timer = null;
  let lastArgs = null;
  let lastThis = null;

  function invoke() {
    fn.apply(lastThis, lastArgs);
    lastArgs = lastThis = null;
  }

  function debounced(...args) {
    lastArgs = args;
    lastThis = this;

    const callNow = leading && !timer;

    if (timer) clearTimeout(timer);

    timer = setTimeout(() => {
      timer = null;
      if (trailing && lastArgs) invoke();
    }, delay);

    if (callNow) invoke();
  }

  debounced.cancel = () => {
    clearTimeout(timer);
    timer = null;
    lastArgs = lastThis = null;
  };

  return debounced;
}

實作細節

  • 使用閉包保存 timer,讓每次呼叫 debounced 函式共享同一個計時器。
  • fn.apply(this, args) 保留原函式的 this 與參數。
  • React 中要用 useCallback / useRef 包裝,避免每次 render 產生新的 debounced 函式。

Debounce vs Throttle

比較 Debounce Throttle
策略 最後一次觸發後才執行 每隔固定時間執行一次
適用 搜尋、resize、輸入驗證 scroll、mousemove、射擊遊戲
類比 電梯:最後一個人進來後才關門 水龍頭:固定流速滴水

✦ AI 模擬面試

輸入你的答案,AI 即時分析精準度與改進空間

登入後即可使用 AI 評分