防抖(debounce)
防抖是一种优化技术,用于限制某些高频率事件(窗口大小调整,滚动,输入框输入调用后台接口等)的触发次数。设置一个给定的延迟时间,如果在这个延迟时间内该事件没有再次触发,则重新开始计时,在延迟时间结束后,事件才会被真正处理。
例如电梯关门需要5秒后执行,进来一个人,然后就需要等待5秒,再进来一个人再等待5秒,这就是防抖。
function debounce(func, wait) {
let timeout;
return function (...args) {
// context 保存了返回函数调用时的 this 值
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}
理解上述方法
- 如果
timeout
不为空,表示正在执行setTimeout
,但是还没有执行到回调函数,此时要清除计时器 - 进而取消执行
setTimeout
- 最后一次执行,执行了一个新的定时器,此时在等待时间内没有新的事件触发,定时器到期,执行回调函数。
// 使用
const obj = {
value: 0,
increment() {
this.value++;
console.log(this.value);
}
};
const debouncedIncrement = debounce(obj.increment, 500);
obj.debouncedIncrement = debouncedIncrement;
// 调用防抖方法,this指向 obj
obj.debouncedIncrement(); // 500毫秒后输出1
我们需要保存当前的this
,如果不保存的话,在下面这个例子中,this
就会指向window
对象,这样就无法调用this.value
了。
具体执行步骤及其细节
- 14行(第二个代码片段)中,调用防抖,此时
this
指向obj
- 在上述的防抖方法中(第一个代码片段,第6行),
context
此时被赋值为obj
- 当500毫秒过去后,执行
func.apply(context, args)
func
是obj.increment
。
<input type="text" id="searchBox" placeholder="Type to search...">
function handleSearch(event, extraParam) {
console.log('Searching for:', event.target.value);
console.log('Extra parameter:', extraParam);
}
const searchBox = document.getElementById('searchBox');
const debouncedSearch = debounce(handleSearch, 500);
searchBox.addEventListener('input', (event) => debouncedSearch(event, 'someExtraValue'));
-
用户在输入框中输入时,每次输入都会触发
input
事件,调用debouncedSearch
函数。 -
debouncedSearch
函数捕获所有传递的参数(在这个例子中是event
对象和'someExtraValue'
)。 -
当定时器到期后,调用
fn.apply(this, args)
,确保handleSearch
函数接收到正确的参数。 -
handleSearch
函数在 500 毫秒后被执行,并输出event.target.value
和'someExtraValue'
。
节流(throttle)
节流会确保在制定的时间间隔内发生至少一次。
这个代码比较好理解,时间间隔大于wait的时候,就可以执行一次
function throttle(func, wait) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= wait) {
lastTime = now;
func.apply(this, args);
}
};
}
有点类似于平常写的,防止重复点击的操作
function throttle(fn, wait) {
let bool = true
return function (...arg) {
if (!bool) return
bool = false
setTimeout(() => {
fn.call(this, arg)
bool = true
}, wait)
}
}