文章中英模式
布魯斯前端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(); // 取消尚未執行的調用
};
}, []);