跳到主要内容
全部已验证问答

const search = debounce(async (keyword: string) => { const res = await fetch(`/api/search?q=${encodeURIComponent(keyword)}`); console.log(await res.json()); }, 300); document.querySelector<HTMLInputElement>('#search')! .addEventListener('input', (event) => { search(event.currentTarget.value); }); 这么去使用防抖是不是每一次的修改都会新建search(event.currentTarget.value); 方法,然后在 debounce 内容不就相当于重新创建 settimeout 了。

作者已验证2026-08-14 · 回答由 AI 分身生成,作者本人盖章认可

不会。这里要区分创建函数调用函数

search(event.currentTarget.value);

只是调用已经创建好的 search 函数,并传入新的参数,不会重新创建 searchsearch 只在下面这行执行 debounce(...) 时创建一次:

const search = debounce(async (keyword: string) => {
  // ...
}, 300);

debounce 内部通过闭包保存同一个 timer

function debounce<T extends (...args: any[]) => void>(fn: T, wait: number) {
  let timer: ReturnType<typeof setTimeout> | undefined;

  return function (...args: Parameters<T>) {
    if (timer) clearTimeout(timer); // 取消上一次定时任务

    timer = setTimeout(() => {      // 创建本次定时任务
      fn(...args);
    }, wait);
  };
}

调用过程大致是:

输入 a
→ 创建定时器 1

100ms 后输入 ab
→ 清除定时器 1
→ 创建定时器 2

100ms 后输入 abc
→ 清除定时器 2
→ 创建定时器 3

之后 300ms 没有输入
→ 定时器 3 执行
→ 只请求 abc

所以你的理解有一半是对的:

  • 每次输入不会创建新的 search 函数
  • 每次调用确实会清除旧 timer,再创建一个新的 timer;
  • 这正是防抖的核心:不断重新计时,最终只执行最后一次回调【1】【2】。

可以验证 search 始终是同一个函数:

const first = search;

search('a');
search('ab');
search('abc');

console.log(first === search); // true

真正错误的是把 debounce 放进事件回调里:

input.addEventListener('input', (event) => {
  // 错误:每次 input 都创建一个新的防抖函数,
  // 每个函数都有自己独立的 timer,旧 timer 无法被清除
  const search = debounce(request, 300);
  search(event.currentTarget.value);
});

这样每次输入都会产生新的闭包和独立的 timer,最终可能每个请求都执行,防抖就失效了。

正确方式就是你原来的写法:先创建一次防抖函数,再反复调用它

const search = debounce(request, 300);

input.addEventListener('input', (event) => {
  search(event.currentTarget.value);
});

如果是在 React 函数组件中,还要避免每次 render 都重新创建防抖函数,通常用 useMemouseRef 保持实例稳定:

const search = useMemo(
  () =>
    debounce(async (keyword: string) => {
      const res = await fetch(
        `/api/search?q=${encodeURIComponent(keyword)}`
      );
      console.log(await res.json());
    }, 300),
  []
);

useEffect(() => {
  return () => search.cancel();
}, [search]);

核心判断标准只有一个:debounce(...) 应该执行一次,返回的防抖函数可以调用很多次。

对这个话题还有疑问?

接着问分身