场景
每次resize/scroll触发统计事件
文本输入验证(连续输入文字后发生AJAX请求进行验证,验证一次就好)
加载更多
函数防抖
概念以及原理:
函数执行一次过后,在设置好的一段时间内不在执行;
如果在等待时间内手动触发该函数,那么将重新计算等待时间。
加深概念:
坐电梯,众所周知我们进电梯需要等几秒电梯门才会自动关闭,如果在等待这几秒又有人进来了,那么等待的时候就会重新计算,当然排除我们手动关闭电梯的情况
代码实现(滚动条滚动案例)
<script>
var n = 0;
function debounce (method, wait) {
var timer = null;
var context, args;
return function () {
context = this;
args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
method.apply(context, args)
}, wait)
}
}
window.onscroll = debounce (function () {
console.log(1)
}, 500)
</script>
结果:**只会在最后一次触发的时候执行回调,**因为每次调用,都会通过clearTimeout清除掉上一个timer。
思考:这个封装似乎有点问题,如果我想在第一次触发的时候执行回调呢?这种场景适不适合加载更多场景?因为从体验上看,我们希望第一次触发加载更多的时候,就执行回调。而不是在最后一次触发执行回调。如果我们一直滚动页面那么将一直不会触发回调,只有停止滚动才会触发……这样体验就太糟糕了。
代码优化:
基于上面的问题,我们进行再一次封装,多加一个参数。
现假设函数的定义形式为:
<script>
var n = 0;
function debounce (method, wait, flag) {
var timer = null;
var context, args, result, timestamp;
var later = function () {
var oDate = new Date();
var last = oDate.getTime() - timestamp; // 计算第一次时间戳与当前时间戳的差值。
if (last < wait && last >= 0 ) { // 在等待时间内触发此函数,重新计时。
timer = setTimeout(later, wait - last);
} else {
timer = null;
if (!flag) { // 限制flag 为true时,执行回调函数。
result = method.apply(context, args);
if (!timer) {
context = args = null
}
}
}
}
return function () {
var oDate = new Date();
var callNow = flag && !timer; // 代表第一次调用立即执行。
timestamp = oDate.getTime(); // 记录下当前时间戳
context = this;
args = arguments;
if (!timer) { // 第一次触发时,timer为空,进入此分支
timer = setTimeout(later, wait);
}
if (callNow) { // 第一次触发且flag为true,进入此分支
result = method.apply(context, args);
context = args = null
}
return result;
}
}
window.onscroll = debounce (function () {
console.log(1)
}, 500, false)
</script>
此次封装支持两种形式:
flag为true时,连续事件触发时,只会在第一次触发的时候执行回调。
flag为false时,连续事件触发时,只会在最后一次触发的时候执行回调。