鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - 实现防抖函数处理输入事件

学习如何实现防抖函数来优化表单输入、搜索建议和按钮点击等事件,提升用户体验与前端效能。

影片縮圖

懒得看文章?那就来看视频吧

什么是防抖 (Debounce)?

防抖是一种前端优化技术,用于控制函数触发的频率,尤其适用于处理用户快速连续操作,如:输入框打字、窗口调整大小、滚动事件等。

核心原理:延迟执行函数,若指定时间内触发多次,则以最后一次操作为准。

防抖应用场景

  • 1. 搜索框输入建议(等用户停止输入后才发送请求)
  • 2. 表单验证(用户输入完毕后再验证)
  • 3. 窗口调整大小时的重新计算
  • 4. 避免按钮重复点击提交
  • 5. 即时保存文章草稿

基本实现:防抖函数 (Debounce)

下面是一个简单的防抖函数实现,接收一个函数和延迟时间作为参数:

function debounce(func, delay = 300) {
  let timer = null;
  
  return function(...args) {
    // 保存 this 上下文
    const context = this;
    
    // 清除之前的计时器
    clearTimeout(timer);
    
    // 设置新的计时器
    timer = setTimeout(() => {
      func.apply(context, args);
    }, delay);
  };
}

使用示例:搜索输入框

// 定义一个搜索函数
function search(query) {
  console.log("搜索:", query);
  // 通常这里会有 API 请求
}

// 创建防抖版本的搜索函数 (500ms延迟)
const debouncedSearch = debounce(search, 500);

// 绑定到输入事件
document.querySelector('#search-input').addEventListener('input', function(e) {
  debouncedSearch(e.target.value);
});

进阶实现:带立即执行选项的防抖

有时我们需要首次触发立即执行,之后再进行防抖,例如:表单验证时希望立即反馈首次输入。

function advancedDebounce(func, delay = 300, immediate = false) {
  let timer = null;
  
  return function(...args) {
    const context = this;
    const callNow = immediate && !timer;
    
    clearTimeout(timer);
    
    timer = setTimeout(() => {
      timer = null;
      
      if (!immediate) {
        func.apply(context, args);
      }
    }, delay);
    
    // 如果是立即执行模式且首次触发,立即调用函数
    if (callNow) {
      func.apply(context, args);
    }
  };
}

使用范例:表单验证

// 验证电子邮件格式
function validateEmail(email) {
  const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,6}$/;
  const isValid = emailRegex.test(email);
  
  if (!isValid) {
    document.querySelector('#email-error').textContent = '请输入有效的电子邮件';
  } else {
    document.querySelector('#email-error').textContent = '';
  }
}

// 立即验证首次输入,之后等用户输入完毕再验证
const debouncedValidate = advancedDebounce(validateEmail, 500, true);

document.querySelector('#email-input').addEventListener('input', function(e) {
  debouncedValidate(e.target.value);
});

防抖函数处理常见问题

1. 如何处理 this 绑定问题

在类组件中使用防抖时,需要正确处理 this 的绑定,可以使用箭头函数或在构造函数中绑定。

class SearchComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { query: '' };
    
    // 绑定 this 到防抖后的方法
    this.debouncedSearch = debounce(this.search.bind(this), 500);
    // 或直接使用箭头函数
    // this.search = () => { /* ... */ };
    // this.debouncedSearch = debounce(this.search, 500);
  }
  
  search() {
    // 这里可以安全地使用 this
    console.log('搜索:', this.state.query);
  }
  
  handleChange = (e) => {
    this.setState({ query: e.target.value });
    this.debouncedSearch();
  }
  
  render() {
    return (
      <input 
        type="text" 
        value={this.state.query} 
        onChange={this.handleChange} 
      />
    );
  }
}

2. 取消防抖执行

有时我们需要取消延迟执行,例如组件卸载时取消尚未执行的调用:

function debounceWithCancel(func, delay = 300) {
  let timer = null;
  
  function debounced(...args) {
    const context = this;
    clearTimeout(timer);
    
    timer = setTimeout(() => {
      func.apply(context, args);
    }, delay);
  }
  
  // 添加取消方法
  debounced.cancel = function() {
    clearTimeout(timer);
    timer = null;
  };
  
  return debounced;
}

// 在 React 组件中使用
useEffect(() => {
  const debouncedFunc = debounceWithCancel(myFunction, 500);
  
  // 注册事件监听
  element.addEventListener('input', debouncedFunc);
  
  // 清理
  return () => {
    element.removeEventListener('input', debouncedFunc);
    debouncedFunc.cancel(); // 取消尚未执行的调用
  };
}, []);