场景

有一个输入框,需要根据输入内容去更新数据,这里有个问题若是用 change,那得失去焦点才能拿到数据,若是用 keyup 又会每次按一个键都更新。遇到类似这种问题我们就可以用防抖来解决了,只要用户还在输入,就不执行更新数据的操作,当用户停止操作时间 n 之后再去做处理,这样就可以达到一个输入优化的效果了。

实现

先来看一个最简单的实现方法

function debounce(func, wait) {
var timeout;
return function () {
clearTimeout(timeout)
timeout = setTimeout(func, wait);
}
}

上述 demo 利用闭包的特性,把 timeoutId 存在了上层作用域的 timout 变量里,每次处理返回的函数都拿得到这个 timeoutId,从而可以做到取消定时的功能。按照上面的描述,我们需要做到最后一次输入时间n之后才去执行处理数据的程序,所以这里用了定时器,延迟程序的执行,每次输入都取消掉上一次的定时任务,所以定时任务只会在最后一次输入后时间n才会执行。

接下来有个问题,我们的函数需要拿到一些参数,根据参数去更新数据,上述 demo 是无法拿到参数的,这里我们可以使用 apply 去实现,接下来看一下带参数的 demo

function debounce(func, wait) {
var timeout;
return function () {
var context = this
var args = arguments;
clearTimeout(timeout)
timeout = setTimeout(()=>{
func.apply(this,args)
}, wait);
}
}

至此,上述功能基本实现了,但是要输入完后等时间n才能执行,能不能先执行程序,间隔时间 n 再触发下一次执行呢?再改进一下 demo

function debounce(func, wait, immediate) {
var timeout, result;

return function () {
var context = this;
var args = arguments;

if (timeout) clearTimeout(timeout);
if (immediate) {
// 如果已经执行过,不再执行
var callNow = !timeout;
timeout = setTimeout(function () {
timeout = null;
}, wait);
if (callNow) result = func.apply(context, args);
} else {
timeout = setTimeout(function () {
func.apply(context, args);
}, wait);
}
return result;
};
}

完整 demo:

See the Pen debounce by Anna (@AnnaLoveLife) on CodePen.