推荐答案
// www.javascriptcn.com code example
function debounce(func, delay) {
let timerId;
return function(...args) {
clearTimeout(timerId);
timerId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}本题详细解读
防抖函数的核心概念
防抖 (Debounce) 是一种常用的优化技巧,用于限制某个函数在短时间内被频繁调用的次数。其核心思想是:将多次触发合并成一次执行。如果持续触发事件,在设定的延迟时间内没有再次触发,则执行函数;如果在延迟时间内再次触发,则重新计时。常见应用场景包括:
- 输入框搜索提示: 当用户在输入框输入时,不立即发送请求,而是等待用户停止输入一段时间后再发送请求,减少服务器压力。
- 窗口resize事件: 避免窗口频繁调整大小时,导致处理函数被多次调用,造成性能问题。
- 按钮点击: 防止用户快速多次点击按钮,产生意外操作。
代码详解
闭包:
debounce函数返回一个内部函数,形成闭包,使得timerId变量在内部函数中能够被保留和修改。timerId变量: 用于存储setTimeout返回的定时器 ID,以便清除之前的定时器。return function(...args): 返回的内部函数是实际被调用的函数,接收任意数量的参数...args。clearTimeout(timerId): 每次触发事件时,先清除之前的定时器,确保只有一个定时器在运行。setTimeout(() => { ... }, delay): 设置一个新的定时器。当延迟时间delay结束后,执行传入的函数func。func.apply(this, args): 使用apply方法调用传入的函数,并传递正确的this上下文和参数。
使用示例
// www.javascriptcn.com code example
function handleInput(value) {
console.log('处理输入:', value);
}
const debouncedHandleInput = debounce(handleInput, 300); // 延迟 300 毫秒
// 模拟输入事件
debouncedHandleInput('a');
debouncedHandleInput('ab');
setTimeout(() => {
debouncedHandleInput('abc');
}, 100); // 在 100ms 后触发
setTimeout(() => {
debouncedHandleInput('abcd');
}, 200) // 在 200ms 后触发
setTimeout(() => {
debouncedHandleInput('abcde');
}, 400) // 在 400ms 后触发
// 只有最后一次输入事件(`abcde`)会在 300ms 后被执行防抖和节流的区别
防抖和节流都是为了优化频繁触发的事件,但其实现方式和应用场景有所不同:
- 防抖 (Debounce): 只在最后一次触发事件后延迟执行。如果持续触发,则重新计时。适用于只需要最后一次触发结果的场景,如输入框搜索。
- 节流 (Throttle): 在一段时间内,只执行一次函数。适用于需要控制函数执行频率的场景,如滚动事件。
扩展:带有立即执行的防抖函数
// www.javascriptcn.com code example
function debounce(func, delay, immediate = false) {
let timerId;
let callNow = false;
return function(...args) {
if(immediate && !timerId) {
func.apply(this, args);
callNow = true;
}
clearTimeout(timerId);
timerId = setTimeout(() => {
if (!immediate || callNow){
func.apply(this, args);
}
timerId = null;
callNow = false;
}, delay);
};
}通过添加 immediate 参数,可以实现立即执行的版本。当 immediate 为 true 时,在第一次触发事件时会立即执行函数,之后才会进行防抖延迟,并且只有在定时器时间结束后才会再次执行。