Skip to content

防抖、节流是什么?区别和应用场景?

防抖和节流都是用来控制函数执行频率的优化手段

防抖

shell
# 什么时候防抖?
连续触发只执行一次

# 使用场景?
搜索框输入、窗口尺寸调整、按钮重复提交

# 实现:基础防抖 + 立即执行状态的防抖函数
function debounce(func, delay, immediate = false) {
  let timer = null;
  return function(...args) {
    const context = this;
    
    if (immediate && !timer) {
      func.apply(context, args);
    }
    
    if (timer) clearTimeout(timer);
    
    timer = setTimeout(() => {
      if (!immediate) {
        func.apply(context, args);
      }
      timer = null;
    }, delay);
  };
}

节流

shell
# 什么时候节流?
连续触发按固定频率执行

# 使用场景?
滚动监听、拖拽位置更新、搜索联想

# 实现:基础节流 + 确保最后一次执行
function throttle(func, delay) {
  let timer = null;
  let previous = 0;
  
  return function(...args) {
    const context = this;
    const now = Date.now();
    const remaining = delay - (now - previous);
    
    // 超过时间间隔或时间调整(剩余时间小于0)
    if (remaining <= 0 || remaining > delay) {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      previous = now;
      func.apply(context, args);
    } else if (!timer) {
      timer = setTimeout(() => {
        previous = Date.now();
        timer = null;
        func.apply(context, args);
      }, remaining);
    }
  };
}

附:基础防抖、定时器与时间戳节流

防抖和节流常用来收敛短时间内反复触发的事件(如滚动、输入),减少实际执行次数,从而提高性能

提示

防抖:短时间内连续触发只是执行一次

节流:每隔一段固定时间执行一次

实现防抖

指定时间内触发重新计时,指定时间外触发执行回调

  • 定义防抖函数把目标方法作为参数传入
  • 函数闭包保留状态:函数内部返回函数,在闭包函数外部保留定时器初始状态
  • 设置定时器
  • 定时时间内如果定时器存在则清理定时器重新开始计时,过了定时器时间则执行目标方法
js
// 使用防抖函数
const el = document.getElementById("debounce");
el.addEventListener(
  "click",
  debounce(function () {
    console.log(" hello world ");
  }, 1000)
);

// 通过 setTimeout 实现
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, delay);
  };
}

实现节流

指定时间内若已触发过则按间隔执行,常用定时器或时间戳两种思路。

  1. 方案一:定时器实现
    • 定义节流函数把目标方法作为参数传入
    • 函数闭包保留状态:函数内部返回函数,在闭包函数外部保留定时状态
    • 设置定时器
    • 定时时间内如果定时器存在什么也不做(if 分支),过了定时器时间则执行目标方法并清理定时器
js
// 通过 setTimeout 实现
function throttled2(fn, delay = 500) {
   let timer = null;
   return function (...args) {
      if (!timer) {
         timer = setTimeout(() => {
            fn.apply(this, args);
            timer = null;
         }, delay);
      }
   };
}
  1. 方案二:时间戳实现
    • 定义节流函数把目标方法作为参数传入
    • 函数闭包保留状态:函数内部返回函数,在闭包函数外部记录上次执行时间
    • 根据时间差决定是否执行目标方法和更新时间
ts
// 通过时间戳实现
function throttled1(fn, delay = 500) { 
  let oldtime = Date.now();
  return function (...args) {
    let newtime = Date.now();
    if (newtime - oldtime >= delay) {
      fn.apply(null, args);
      oldtime = Date.now();
    }
  };
}

防抖和节流的使用场景

  1. 防抖的使用场景?

    • 输入框实时搜索
    • 表单验证
    • 按钮
  2. 节流的使用场景?

    • 滚动事件监听
    • 搜索框联想