好的,阿杰 👍 我给你系统整理一篇 JavaScript 进阶篇——函数防抖(debounce)详解,包含概念、应用场景、原理代码和升级版本。


🚀 JavaScript 进阶篇 —— 函数防抖(debounce)

1. 什么是函数防抖?

函数防抖(debounce)是一种常见的 性能优化技巧
在高频触发的事件中(如输入框输入、滚动、窗口大小调整),我们不希望函数被频繁调用,而是 只在最后一次操作结束后的一段时间内执行

👉 核心思想:你一直触发我就不执行,等你不动了再执行一次


2. 应用场景

  • 输入搜索框:用户输入时不断触发 input 事件,但只在用户停止输入后再发起请求。
  • 窗口 resize:调整浏览器大小时触发很多次 resize,只在调整结束后重新计算布局。
  • 按钮点击防重复:防止用户在短时间内多次点击按钮,避免重复提交。

3. 基础实现

function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

// 使用示例:搜索框输入
const input = document.querySelector("#search");
input.addEventListener("input", debounce(function(e) {
  console.log("搜索内容:", e.target.value);
}, 500));

解释:

  1. 每次触发事件,先清除定时器 clearTimeout(timer)
  2. 重新开启定时器,等 delay 毫秒后执行函数。
  3. 如果用户持续输入,定时器就一直被清掉,直到停下来。

4. 立即执行版(leading)

有时我们希望函数在第一次触发时立即执行,然后等用户停下来不再触发。

function debounce(fn, delay, immediate = false) {
  let timer = null;
  return function(...args) {
    if (timer) clearTimeout(timer);

    if (immediate && !timer) {
      fn.apply(this, args);
    }

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

  • immediate = true → 先执行一次,再防抖。
  • immediate = false → 延迟执行(常见场景)。

5. 带返回值的防抖

有时候我们需要 拿到函数的返回值,就要用 Promise 封装:

function debounce(fn, delay, immediate = false) {
  let timer = null;
  return function(...args) {
    return new Promise((resolve) => {
      if (timer) clearTimeout(timer);

      if (immediate && !timer) {
        resolve(fn.apply(this, args));
      }

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


6. 防抖 vs 节流(throttle)

⚡ 防抖(debounce):事件触发后 只执行最后一次
⚡ 节流(throttle):事件触发后 按固定时间间隔执行

📌 对比例子:

  • 防抖:电梯按钮,只有最后一次按下才有效。
  • 节流:公交车,每隔 5 分钟发车一次,不管有多少人等。

✅ 总结

  • 防抖核心:clearTimeout + setTimeout
  • 适合 输入框、resize、滚动停止 等场景。
  • 可扩展:支持立即执行、返回值、Promise 封装。

好👌 阿杰,我帮你整理一个 可复用的防抖(debounce)+ 节流(throttle)工具库,保存成 utils.js 就能在项目里随时用。


📦 utils.js

/**
 * 函数防抖 debounce
 * @param {Function} fn - 需要防抖的函数
 * @param {number} delay - 延迟时间(ms)
 * @param {boolean} immediate - 是否立即执行
 * @returns {Function}
 */
export function debounce(fn, delay = 300, immediate = false) {
  let timer = null;
  return function (...args) {
    const context = this;

    if (timer) clearTimeout(timer);

    if (immediate && !timer) {
      fn.apply(context, args);
    }

    timer = setTimeout(() => {
      timer = null;
      if (!immediate) fn.apply(context, args);
    }, delay);
  };
}

/**
 * 函数节流 throttle
 * @param {Function} fn - 需要节流的函数
 * @param {number} delay - 时间间隔(ms)
 * @param {boolean} leading - 是否立即执行一次
 * @param {boolean} trailing - 是否在最后执行一次
 * @returns {Function}
 */
export function throttle(fn, delay = 300, leading = true, trailing = true) {
  let lastCall = 0;
  let timer = null;

  return function (...args) {
    const context = this;
    const now = Date.now();

    if (!lastCall && !leading) lastCall = now;

    const remaining = delay - (now - lastCall);

    if (remaining <= 0) {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      lastCall = now;
      fn.apply(context, args);
    } else if (!timer && trailing) {
      timer = setTimeout(() => {
        lastCall = leading ? Date.now() : 0;
        timer = null;
        fn.apply(context, args);
      }, remaining);
    }
  };
}


🔧 使用方法

1. 防抖(输入框搜索)

import { debounce } from './utils.js';

const input = document.querySelector('#search');
input.addEventListener('input', debounce((e) => {
  console.log('搜索:', e.target.value);
}, 500));

👉 用户停止输入 500ms 后才触发。


2. 节流(页面滚动)

import { throttle } from './utils.js';

window.addEventListener('scroll', throttle(() => {
  console.log('滚动位置:', window.scrollY);
}, 1000));

👉 每隔 1 秒最多触发一次,避免频繁触发。


3. 点击按钮防止连点

import { debounce } from './utils.js';

document.querySelector('#btn').addEventListener('click', debounce(() => {
  console.log('只执行一次提交');
}, 1000, true)); // 立即执行版


✨ 这样以后你直接 import { debounce, throttle } 就能用,方便在各种项目里复用。