防抖与节流的封装函数与使用详解
防抖与节流
为什么使用防抖节流?
在前端开发中有一部分的用户行为会频繁的触发事件执行,而对于DOM操作、资源加载等耗费性能的处理,很可能导致界面卡顿,甚至浏览器的崩溃。函数节流(throttle)和函数防抖(debounce)就是为了解决类似需求应运而生的。
防抖(debounce)
函数防抖就是在函数需要频繁触发情况时,只有足够空闲的时间,才执行一次。好像公交司机会等人都上车后才关门一样。他不会上来一个人就触发一次关门,而是等人陆续上来后等待一会再触发关门
场景:(当时间频繁触发后停止一段时间执行)
- 实时搜索(keyup)
- 拖拽( mousemove )
- 手机号、邮箱验证输入检测
- 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
我们以百度搜索框在输入完成后停顿一段时间便会进ajax请求为例子,我们实现输入框停止输入后再打印
//使用高阶函数,利用闭包封装
<!-- <script src="../../plugin/helpers.js"></script> -->
let oInp = document.getElementById('inp')
let timer = null
const debounce = (handler, delay) => {
let timer = null //利用闭包保存同一个timer
return () => {
let _self = this //取debounce执行作用域的this
let _arg = arguments //利用闭包保存参数数组
clearTimeout(timer) //不断的执行函数不断的清除定时器
timer = setTimeout(() => {
handler.apply(_self, _arg) //用apply指向调用debounce的对象,相当于_this.handler(args);
}, delay)
}
}
let shouValue = (e) => {
console.log(e,this.value)
}
oInp.oninput = debounce(shouValue, 1000);
节流(throttle)
函数节流就是预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。好像水滴攒到一定重量才会落下一样。
场景: CON(不管时间触发的多么频繁,至少要每隔一段时间执行而不是等事件触发完了等一段时间执行)
窗口调整(resize)
页面滚动( scroll)
抢购疯狂点击( mousedown)
目前我有两种思路:
- 使用定时器,定时一秒钟之后去执行,但是在这1s中不停的调用,不让他的定时器清零重新计时,不会影响当前的结果,还是那1s继续等,等1秒时触发(会出现停止操作还是会触发)
//保证一个时间段内执行一次
const throttle = (handler, time) => {
let timer
return () => {
if (timer) {
return //判断如果有计时器不清零直接返回啥也不做
}
let _self = this //取throttle 执行作用域的this
let _arg = arguments //利用闭包保存参数数组
timer = setTimeout(() => {
handle.apply(_self, _arg)
timer = null
}, time)
}
}
//触发事件
window.onresize = () => handle()
//处理函数
let test = () =>console.log("a")
//调用throttle函数传参
let handle = myPlugin.throttle(test, 2000)
- 使用时间戳,先立即执行,只不过在下一次执行要等一段时间
const throttle = (handler, time) => {
let t
return () => {
let _self = this //取throttle 执行作用域的this
let _arg = arguments //利用闭包保存参数数组
if (!t || Date.now() - t >= time ) {
handler.apply(_self , _arg );
t = Date.now(); //得到的当前时间戳
}
}
}
window.onresize = () => handle()
let test = () =>console.log("a")
let handle = myPlugin.throttle(test, 2000)
判断两种方式差异,我们只需要判断是否需要立即执行,我们就可以把节流的两种合并在一起
//保证一段时间只执行一次
constthrottle = (handler, time, immediately) => {
if (immediately === undefined) {
immediately = true //判断需要先立即执行
}
if (immediately) {
let t
return () => {
let _self = this //取throttle 执行作用域的this
let _arg = arguments //利用闭包保存参数数组
if (!t || Date.now() - t >= time) {
handler.apply(_self, _arg);
t = Date.now(); //得到的当前时间戳
}
}
}
else {
let timer
return () => {
if (timer) {
return //判断如果有计时器不清零直接返回啥也不做
}
let _self = this //取throttle 执行作用域的this
let _arg = arguments //利用闭包保存参数数组
timer = setTimeout(() => {
handle.apply(_self, _arg)
timer = null
}, time)
}
}
}
window.onresize = () => handle()
let test = () => console.log("a")
let handle = myPlugin.throttle(test, 2000,true)