# 从手写 debounce 到企业级实现:我在面试中如何“降维打击”面试官

6 阅读3分钟

从手写 debounce 到企业级实现:我在面试中如何“降维打击”面试官

你以为面试官在考你 debounce?
不,他在看你:工程能力 + 抽象能力 + 边界思维


一、面试官到底在考什么?

很多人一上来就开始写:

let timer = null
return function() {
  clearTimeout(timer)
  timer = setTimeout(fn, delay)
}

👉 面试官内心: “嗯,会背”

但他们真正想看的是:

1️⃣ 你有没有抽象能力

  • debounce 的本质是什么?
  • 为什么用闭包?
  • timer 为什么要“复用”?

2️⃣ 你有没有工程思维

  • 能不能支持 leading
  • 能不能 cancel
  • 有没有返回值?
  • this 会不会丢?

3️⃣ 你有没有边界意识

  • 连续触发怎么办?
  • leading + trailing 同时存在?
  • timer 会不会泄漏?

👉 总结一句话:

❌ 会写 debounce ≠ 会设计 debounce


二、基础版:面试第一步(原理表达)

先别写代码,先说清楚:

debounce 的核心是:
通过闭包保存 timer,每次触发时清除上一次定时器,重新计时,只执行最后一次

关键点:

  • 闭包:保存 timer
  • clearTimeout:取消上一次
  • setTimeout:延迟执行
  • apply:绑定 this + 传参

基础实现

function debounce(fn: Function, delay: number) {
  let timer: ReturnType<typeof setTimeout> | null = null

  return function (this: any, ...args: any[]) {
    if (timer) clearTimeout(timer)

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

👉 到这里,只能算“及格”。


三、进阶:面试开始拉开差距

真正的加分点在这里👇


1️⃣ 支持 leading(立即执行)

场景:

  • 用户点击按钮 → 立刻响应
  • 后续防抖

👉 核心点:是否第一次触发

let isInvoked = false

2️⃣ 支持 trailing(停止后执行)

默认行为,但要能控制关闭


3️⃣ cancel 能力

面试官会问:

“如果用户离开页面,还要执行吗?”

👉 必须能手动取消


4️⃣ flush 能力(立即执行)

高级面试题:

“我不想等 delay,能不能马上执行?”


5️⃣ Promise 支持(高阶)

👉 这是很多人不会的点

让 debounce 有返回值


四、企业级 debounce(完整实现)

⚠️ 面试直接甩这个,基本可以结束战斗

type DebounceOptions = {
  leading?: boolean
  trailing?: boolean
}

type DebouncedFn<T extends (...args: any[]) => any> = {
  (...args: Parameters<T>): Promise<ReturnType<T>>
  cancel: () => void
  flush: () => void
}

function debounce<T extends (...args: any[]) => any>(
  fn: T,
  delay: number,
  options: DebounceOptions = {}
): DebouncedFn<T> {
  let timer: ReturnType<typeof setTimeout> | null = null
  let lastArgs: Parameters<T> | null = null
  let lastThis: any = null
  let isInvoked = false

  const { leading = false, trailing = true } = options

  let resolveList: ((value: ReturnType<T>) => void)[] = []

  function invoke() {
    if (!lastArgs) return

    const result = fn.apply(lastThis, lastArgs)

    resolveList.forEach(resolve => resolve(result))
    resolveList = []

    lastArgs = null
    lastThis = null
    isInvoked = true
  }

  const debounced = function (this: any, ...args: Parameters<T>) {
    lastArgs = args
    lastThis = this

    return new Promise<ReturnType<T>>((resolve) => {
      resolveList.push(resolve)

      if (timer) clearTimeout(timer)

      // leading
      if (leading && !isInvoked) {
        invoke()
      }

      timer = setTimeout(() => {
        if (trailing && (!leading || isInvoked)) {
          invoke()
        }
        timer = null
        isInvoked = false
      }, delay)
    })
  } as DebouncedFn<T>

  debounced.cancel = () => {
    if (timer) clearTimeout(timer)
    timer = null
    lastArgs = null
    lastThis = null
    resolveList = []
    isInvoked = false
  }

  debounced.flush = () => {
    if (timer) {
      clearTimeout(timer)
      invoke()
      timer = null
    }
  }

  return debounced
}

五、为什么这才是“企业级”实现?

我们来拆一下能力:

能力是否支持
基础防抖
this 绑定
参数透传
leading
trailing
cancel
flush
Promise 返回值
内存安全

👉 这已经接近 lodash.debounce 的设计了


六、真正的加分点:Hooks 化

如果你在 React 项目里这么说👇

“我在项目中封装了 useDebounce 来处理频繁状态更新”

面试官直接眼前一亮。


简版 useDebounce

import { useEffect, useState } from "react"

export function useDebounce<T>(value: T, delay: number) {
  const [debouncedValue, setDebouncedValue] = useState(value)

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value)
    }, delay)

    return () => clearTimeout(timer)
  }, [value, delay])

  return debouncedValue
}

七、面试“杀招”:你要这样说

最后总结一句你可以直接背的👇


我在项目中不仅实现了基础 debounce,还做了企业级增强:

  • 支持 leading / trailing 控制执行时机
  • 提供 cancel / flush 控制生命周期
  • 使用 Promise 统一返回值
  • 通过闭包管理 timer,避免内存泄漏
  • 并在 React 中封装为 useDebounce,优化频繁状态更新场景

整体设计参考了 lodash 和 ahooks 的实现思想


八、最后的启发(重点)

很多人学技术是这样的:

“这个 API 怎么用?”

但真正厉害的人在想:

“这个 API 为什么这么设计?”


debounce 本质考察的是:

  • 闭包理解
  • 事件模型
  • 异步控制
  • API 设计能力
  • 边界处理能力

结尾

面试不会因为你写了 debounce 给你 offer

但会因为你怎么设计 debounce,决定你是:

  • 初级工程师
  • 还是能写基础库的人