Skip to content

防抖实现

防抖(Debounce)是前端开发中常用的性能优化技术,它可以限制函数的执行频率,避免在短时间内重复执行同一个函数,从而提高页面性能和用户体验。

核心概念

什么是防抖?

防抖是指在一定时间内,多次触发同一个函数,只执行最后一次。例如,在用户输入搜索关键词时,我们可以使用防抖技术,只在用户停止输入一段时间后才发送搜索请求,而不是每次输入都发送请求。

防抖的应用场景

  • 搜索框输入:用户输入时,只在停止输入后才发送搜索请求
  • 窗口 resize:窗口大小改变时,只在调整完成后才执行回调函数
  • 滚动事件:滚动页面时,只在滚动停止后才执行回调函数
  • 按钮点击:防止用户多次点击按钮,只执行最后一次点击

实现原理

防抖的实现原理是:

  1. 当函数被调用时,设置一个定时器
  2. 如果在定时器到期前,函数再次被调用,则重置定时器
  3. 当定时器到期时,执行函数

实现代码

基础版本

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, immediate = false) {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    const later = function() {
      timeout = null;
      if (!immediate) {
        func.apply(context, args);
      }
    };
    const callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) {
      func.apply(context, args);
    }
  };
}

优化版本(支持取消)

javascript
function debounce(func, wait, immediate = false) {
  let timeout;
  const debounced = function() {
    const context = this;
    const args = arguments;
    const later = function() {
      timeout = null;
      if (!immediate) {
        func.apply(context, args);
      }
    };
    const callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) {
      func.apply(context, args);
    }
  };
  debounced.cancel = function() {
    clearTimeout(timeout);
    timeout = null;
  };
  return debounced;
}

使用示例

搜索框输入防抖

javascript
// HTML: <input type="text" id="search" placeholder="搜索...">

const searchInput = document.getElementById('search');

function handleSearch(e) {
  console.log('搜索:', e.target.value);
  // 发送搜索请求
}

// 使用防抖,300ms后执行搜索
const debouncedSearch = debounce(handleSearch, 300);

searchInput.addEventListener('input', debouncedSearch);

窗口 resize 防抖

javascript
function handleResize() {
  console.log('窗口大小:', window.innerWidth, 'x', window.innerHeight);
  // 执行 resize 相关操作
}

// 使用防抖,500ms后执行 resize 操作
const debouncedResize = debounce(handleResize, 500);

window.addEventListener('resize', debouncedResize);

按钮点击防抖

javascript
// HTML: <button id="submit">提交</button>

const submitButton = document.getElementById('submit');

function handleSubmit() {
  console.log('提交表单');
  // 提交表单
}

// 使用防抖,1000ms内只执行一次点击
const debouncedSubmit = debounce(handleSubmit, 1000, true);

submitButton.addEventListener('click', debouncedSubmit);

取消防抖

javascript
// 取消防抖
function cancelDebounce() {
  debouncedSearch.cancel();
  console.log('防抖已取消');
}

// HTML: <button id="cancel">取消搜索</button>
document.getElementById('cancel').addEventListener('click', cancelDebounce);

性能优化

1. 使用 requestAnimationFrame 代替 setTimeout

对于需要频繁执行的动画或视觉效果,可以使用 requestAnimationFrame 代替 setTimeout,以获得更好的性能。

javascript
function debounceRAF(func) {
  let frame;
  return function() {
    const context = this;
    const args = arguments;
    cancelAnimationFrame(frame);
    frame = requestAnimationFrame(() => {
      func.apply(context, args);
    });
  };
}

2. 使用闭包缓存参数

对于需要频繁调用的函数,可以使用闭包缓存参数,以减少函数调用的开销。

javascript
function debounceWithCache(func, wait) {
  let timeout;
  let lastArgs;
  let lastThis;
  return function() {
    lastThis = this;
    lastArgs = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(lastThis, lastArgs);
    }, wait);
  };
}

面试常见问题

1. 防抖和节流的区别是什么?

答案示例: 防抖和节流都是前端开发中常用的性能优化技术,它们的区别在于:

  • 防抖:在一定时间内,多次触发同一个函数,只执行最后一次。例如,用户输入搜索关键词时,只在停止输入后才发送搜索请求。
  • 节流:在一定时间内,多次触发同一个函数,只执行一次。例如,滚动页面时,每 100ms 只执行一次回调函数。

2. 如何实现一个防抖函数?

答案示例: 实现防抖函数的核心原理是使用定时器,当函数被调用时,设置一个定时器,如果在定时器到期前,函数再次被调用,则重置定时器,当定时器到期时,执行函数。

javascript
function debounce(func, wait, immediate = false) {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    const later = function() {
      timeout = null;
      if (!immediate) {
        func.apply(context, args);
      }
    };
    const callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) {
      func.apply(context, args);
    }
  };
}

3. 防抖函数的应用场景有哪些?

答案示例: 防抖函数的应用场景包括:

  • 搜索框输入:用户输入时,只在停止输入后才发送搜索请求
  • 窗口 resize:窗口大小改变时,只在调整完成后才执行回调函数
  • 滚动事件:滚动页面时,只在滚动停止后才执行回调函数
  • 按钮点击:防止用户多次点击按钮,只执行最后一次点击
  • 表单验证:用户输入时,只在停止输入后才进行表单验证

4. 如何优化防抖函数的性能?

答案示例: 优化防抖函数的性能可以从以下几个方面入手:

  1. 使用 requestAnimationFrame 代替 setTimeout:对于需要频繁执行的动画或视觉效果,可以使用 requestAnimationFrame 代替 setTimeout,以获得更好的性能。

  2. 使用闭包缓存参数:对于需要频繁调用的函数,可以使用闭包缓存参数,以减少函数调用的开销。

  3. 避免不必要的计算:在防抖函数中,避免进行不必要的计算,只在必要时才执行函数。

  4. 使用 this 绑定:确保防抖函数中的 this 指向正确,以避免出现上下文丢失的问题。

5. 如何取消防抖函数?

答案示例: 取消防抖函数的方法是清除定时器,具体实现如下:

javascript
function debounce(func, wait, immediate = false) {
  let timeout;
  const debounced = function() {
    const context = this;
    const args = arguments;
    const later = function() {
      timeout = null;
      if (!immediate) {
        func.apply(context, args);
      }
    };
    const callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) {
      func.apply(context, args);
    }
  };
  debounced.cancel = function() {
    clearTimeout(timeout);
    timeout = null;
  };
  return debounced;
}

使用时,可以调用 debounced.cancel() 方法来取消防抖。

总结

防抖是前端开发中常用的性能优化技术,它可以限制函数的执行频率,避免在短时间内重复执行同一个函数,从而提高页面性能和用户体验。

实现防抖函数的核心原理是使用定时器,当函数被调用时,设置一个定时器,如果在定时器到期前,函数再次被调用,则重置定时器,当定时器到期时,执行函数。

防抖函数的应用场景包括搜索框输入、窗口 resize、滚动事件、按钮点击等,它可以有效地减少不必要的函数调用,提高页面性能。

通过学习和使用防抖函数,你将能够更好地优化前端代码,提高用户体验,为面试和工作做好准备。

好好学习,天天向上