节流防抖原理和应用
防抖
原理:事件被触发后不立即触发回调,而是等待 n 秒,如果 n 秒期间事件又被触发则重新计时,并且不会再触发前一事件的回调。
适用场景:
按钮提交场景:防止多次提交按钮,只执行最后一次提交
搜索框联想场景:防止很快打字的时候每个字都发送联想请求,只在用户中断快速输入后发送请求
基础功能,即先防抖后执行
jsfunction debounce(func, wait) { let timeout; return function() { const context = this; const args = arguments; clearTimeout(timeout); timeout = setTimeout(function() { func.apply(context, args); }, wait); }; }
立即执行,即先执行后防抖
有时我们希望做的防抖功能是立即执行,而等 n 秒后才能进行下一次触发,其实这个类似于节流
jsfunction debounce(func, wait, immediate) { let timeout; return function() { const context = this; const args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { const callNow = !timeout; timeout = setTimeout(function() { timeout = null; }, wait); if (callNow) func.apply(context, args); } else { timeout = setTimeout(function() { func.apply(context, args); }, wait); } }; }
返回值功能
func
函数可能有返回值,所以需要返回函数结果,但是当immediate
为false
的时候,由于使用了setTimeout
,我们将func.apply(context, args)
的返回值赋给变量,最后再return
的时候,值将会一直是undifined
,所以只在immediate
为true
的时候返回函数的执行结果。jsfunction debounce(func, wait, immediate) { let timeout, result; return function() { const context = this; const args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { const 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; }; }
节流
原理:规定在一个单位时间内,只能触发一次函数,如果这个单位时间内出发多次函数,只有一次生效。
适用场景
拖拽场景:固定时间内只执行一次,放置超高频次触发位置变动
缩放场景:监控浏览器
resize
使用时间戳实现
- 使用时间戳,当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(一开始设置为 0),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于设置的时间周期则不执行。
jsfunction throttle(func, wait) { let context, args; let previous = 0; return function() { let now = +new Date(); context = this; args = arguments; if (now - previous > wait) { func.apply(context, args); previous = now; } }; }
使用定时器实现
- 当触发事件的时候,我们设置一个定时器,再次触发事件时如果定时器存在就不执行,直到定时器执行,然后执行函数,清空定时器,这样就可以设置下一个定时器。
jsfunction throttle(func, wait) { let timeout; return function() { const context = this; const args = arguments; if (!timeout) { timeout = setTimeout(function() { timeout = null; func.apply(context, args); }, wait); } }; }
CSS节流
CSS 实现和 JS 的思维不同,这种场景可以理解成是对 CSS 动画的控制,比如有一个动画控制按钮从禁用->可点击的变化,每次点击时让这个动画重新执行一遍,在执行的过程中,一直处于禁用状态,是不是就达到了“节流”的效果了
css
button{
animation: throttle 2s step-end forwards;
}
button:active{
animation: none;
}
@keyframes throttle {
from {
pointer-events: none;
}
to {
pointer-events: all;
}
}