防抖(Debounce)

防抖(Debounce)

防抖:在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。

应用场景
  • 搜索框输入(等待用户输入完成后再搜索)
  • 窗口大小调整(调整完成后计算布局)
  • 表单验证(输入完成后验证)
基础版本
javascript 复制代码
function debounce(func, wait) {
  let timeout;
  return function () {
    const context = this;
    const args = arguments;

    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args);
    }, wait);
  };
}
立即执行版本(先执行一次,再防抖)
javascript 复制代码
function debounce(func, wait, imediate) {
  let timeout;
  return function () {
    const context = this;
    const args = arguments;

    if (timeout) clearTimeout(timeout);

    if (imediate) {
      // 如果已经执行过了,不再执行
      const callNow = !timeout;
      timeout = setTimeout(() => {
        timeout = null;
      }, wait);

      if (callNow) {
        func.apply(context, args);
      }
    } else {
      timeout = setTimeout(() => {
        func.apply(context, args);
      }, wait);
    }
  };
}
带返回值版本(需要Promise)
javascript 复制代码
function debounce(func, wait, immediate) {
  let timeout, result;
  const debounced = function () {
    const context = this;
    const args = arguments;

    if (timeout) clearTimeout(timeout);

    if (immediate) {
      const callNow = !timeout;
      timeout = setTimeout(() => {
        timeout = null;
      }, wait);

      if (callNow) {
        result = func.apply(context, args);
      }
    } else {
      timeout = setTimeout(() => {
        func.apply(context, args);
      }, wait);
    }

    return result;
  };

  debounced.cancel = function () {
    clearTimeout(timeout);
    timeout = null;
  };

  return debounced;
}
ES6版本
javascript 复制代码
class Debouncer {
  constructor(func, wait, immediate = false) {
    this.func = func;
    this.wait = wait;
    this.immediate = immediate;
    this.timeout = null;
    this.result = null;
  }

  execute(...args) {
    const context = this;

    if (this.timeout) clearTimeout(this.timeout);

    if (this.immediate) {
      const callNow = !this.timeout;
      this.timeout = setTimeout(() => {
        this.timeout = null;
      }, this.wait);

      if (callNow) {
        this.result = this.func.apply(context, args);
      }
    } else {
      this.timeout = setTimeout(() => {
        this.func.apply(context, args);
      }, this.wait);
    }

    return this.result;
  }

  cancel() {
    clearTimeout(this.timeout);
    this.timeout = null;
  }

  flush() {
    if (this.timeout) {
      clearTimeout(this.timeout);
      this.timeout = null;
      return this.result;
    }
  }
}

const debouncer = new Debouncer((msg) => {
  console.log("防抖执行:", msg, Date.now());
}, 1000);

// 快速调用
debouncer.execute("队列消息1");
debouncer.execute("队列消息2");
debouncer.execute("队列消息3"); // 只有这个会执行
更通用的类实现(同时支持防抖和节流)
javascript 复制代码
class RateLimiter {
  constructor(func, wait, options = {}) {
    this.func = func;
    this.wait = wait;
    this.mode = options.mode || "throttle"; // 'throttle' 或 'debounce'

    this.options = {
      leading: true,
      trailing: true,
      maxWait: null, // 最大等待时间 (防抖专用)
    };
    Object.assign(this.options, options);

    this.timeout = null;
    this.previous = 0;
    this.args = null;
    this.context = null;
    this.lastCallTime = 0;
  }

  execute(...args) {
    const now = Date.now();
    this.context = this;
    this.args = args;
    this.lastCallTime = now;

    if (this.mode === "debounce") {
      return this._debounce(now);
    } else {
      return this._throttle(now);
    }
  }

  _debounce(now) {
    if (this.timeout) clearTimeout(this.timeout);

    // 计算延迟
    let delay = this.wait;

    // 检查是否需要立即执行
    const callNow = this.options.leading && !this.timeout;

    if (callNow) {
      this.previous = now;
      this.func.apply(this.context, this.args);
    }

    this.timeout = setTimeout(() => {
      this.timeout = null;
      if (!callNow || this.options.trailing) {
        this.func.apply(this.context, this.args);
      }
    }, delay);
  }

  _throttle(now) {
    if (!this.previous && this.options.leading === false) {
      this.previous = now;
    }

    const remaining = this.wait - (now - this.previous);

    if (remaining <= 0 || remaining > this.wait) {
      if (this.timeout) {
        clearTimeout(this.timeout);
        this.timeout = null;
      }
      this.previous = now;
      this.func.apply(this.context, this.args);
    } else if (!this.timeout && this.options.trailing !== false) {
      this.timeout = setTimeout(() => {
        this.previous = this.options.leading === false ? 0 : Date.now();
        this.timeout = null;
        this.func.apply(this.context, this.args);
      }, remaining);
    }
  }

  cancel() {
    clearTimeout(this.timeout);
    this.timeout = null;
    this.previous = 0;
  }

  flush() {
    if (this.timeout) {
      clearTimeout(this.timeout);
      this.timeout = null;
      this.func.apply(this.context, this.args);
    }
  }

  pending() {
    return !!this.timeout;
  }
}

// 使用示例
const limiter = new RateLimiter(
  (msg) => console.log(`${msg} at ${Date.now()}`),
  1000,
  { mode: "throttle" } // 或 'debounce'
);

// 切换模式
limiter.mode = "debounce";
相关推荐
冰暮流星18 小时前
javascript如何实现将一个整数倒过来输出
开发语言·前端·javascript
0思必得018 小时前
[Web自动化] 爬虫合规指南:从法律红线到安全实践
前端·爬虫·自动化·web自动化
qq_1777673718 小时前
React Native鸿蒙跨平台实现移动端图书展示与交互系统,涵盖图书列表渲染、多分类筛选、收藏与购物车管理、图书详情展示等核心业务场景
javascript·react native·react.js·ecmascript·交互·harmonyos
Code小翊18 小时前
Vue 3 核心语法速查
前端·javascript·vue.js
雨季66618 小时前
Flutter 三端应用实战:OpenHarmony “安全文本溢出处理调节器”
开发语言·前端·安全·flutter·交互
雨季66618 小时前
Flutter 三端应用实战:OpenHarmony “极简安全文本对齐调节器”
开发语言·前端·javascript·安全·flutter·交互
肖。354878709418 小时前
html选择页最简模板源码,用于集合很多网页!游戏大全数字导航页面,数字选择页制作灵感,已经压缩到最小,现代,讲解。
android·java·javascript·css·html
qq_1777673718 小时前
React Native鸿蒙跨平台完成动漫应用实现本地数据持久化、收藏管理、观看历史记录、标签页切换
javascript·react native·react.js·ecmascript·harmonyos
木子啊18 小时前
Uni-app跨页面通信三剑客
前端·uni-app·传参
tao35566719 小时前
【用AI学前端】准备工作
前端·人工智能