防抖(debounce)和节流(throttle)
防抖(debounce):是指函数触发后一段时间再执行,如果期间重复触发函数,则重新计时,最后只执行一次
案例1:搜索框的提示,如果在输入的时候就触发请求函数获取提示内容,浏览器则会在输入时不断的发送请求,但使用防抖,则可以当输入出现停顿且停顿到一定的时间再执行函数(出现停顿则表示用户的确需要提示,停顿到一定的时间再发送请求获取提示,如果用户快速输入,则说明不需要提示,浏览器也就没必要发送请求去获取提示)
案例2:浏览器的onresize事件,如果在调整浏览器大小时会高频率执行函数,若函数中是dom操作,高频率的dom操作会影响页面显示和浏览器的性能,使用防抖,则在浏览器停止调整后的一段时间再执行函数,减少不必要的dom操作(红宝书上的节流例子就是这个,我觉得可能是节流和防抖并没有分得那么清楚,防抖也属于节流吧)
实现1:红宝书的节流例子
采用定时器,触发就删除上一个定时器,重新创建一个定时器,重新计时
/*fun是待执行函数,delay是指间隔时间,
context指执行上下文,
因为setTimeout里的执行函数
的上下文对象默认为window对象*/
function debounce(fn,delay,context){
clearTimeout(fn.id)
fn.id=setTimeout(fn.bind(context),delay)
}
window.onresize = function(){
debounce(fun,500)
}
//执行函数
function fun(){
}
实现2:采用闭包,自定义实现lodash,实现取消和可立即执行功能。
//使用防抖
e.addEventListener('input',debounce(fun,1000,true))
//防抖函数实现
function debounce = function(fn,delay){
var timer=null//定时器
//待执行函数
var debounced = function(){
var _that = this//保存调用函数的上下文
var _arguments = arguments//保存传入参数
if(timer){
clearTimeout(timer)
}
timer=setTimeout(function(){
fn.apply(_that,_arguments)
},delay)
}
//取消执行
debounced.cancel = function(){
if(timer){
clearTimeout(timer)
timer=null
}
}
//返回待执行函数
return debounced
}
优化是否立即执行,即重复触发函数时,第一次会执行
例如当在搜索框快速输入时,会立即执行一次获取提示,如果之后有停顿且停顿到一定时间才会再执行函数获取提示,后续再触发事件时,仍会立即执行一次,然后同样当出现停顿时再执行一次
function debounce(fn,delay,leading){
var timer=null//定时器
var leading=leading||false//第一次是否立即执行
var debounced = function(){
var _that = this//保存调用函数的上下文
var _arguments = arguments//保存传入参数
if(timer){
clearTimeout(timer)
}
if(leading){
var isInvoke=false//记录是否已立即执行
//无定时器,说明是事件触发的开始,立即执行
if(!timer){
fn.apply(_that,_arguments)
isInvoke=true
}
/*更新定时器,若回调函数最终执行了,则timer为null,
表示已经间隔了足够的时间,下次事件再次触发时会立即执行
(执行上面if语句内的代码)*/
timer=setTimeout(function(){
timer=null
/*如果事件只触发了一次,因为已经立即执行了,
所以不用再执行定时器里的函数*/
if(!isInvoke){
fn.apply(_that,_arguments)
}
},delay)
}
else{
timer=setTimeout(function(){
fn.apply(_that,_arguments)
},delay)
}
}
//取消执行
debounced.cancel = function(){
if(timer){
clearTimeout(timer)
timer=null
}
}
return debounced
}
节流(throttle):在一段时间内重复调用函数,但函数只会执行一次,区别防抖,防抖是触发函数后一般不会立即执行,而是一段时间内不再触发函数时再执行,而节流是调用函数时会立即执行,但如果和上次执行的函数时间间隔不满足要求则不会执行。
案例1:小游戏中点击按钮飞机发射子弹时,重复点击按钮,一定时间内飞机也只发射一颗子弹
实现1:采用定时器和一个开关
function throttle(fn,interval){
var timer=null,flag=true//flag作为一个开关,为true则执行函数
function throttled(){
var _this=this,
_arguments=arguments
if(flag){
//flag为true,执行函数,并在一定时间后再恢复为true
flag=false
fn.apply(_this,_arguments)
setTimeout(()=>{
flag=true
},interval)
}
}
return throttled
}
优化是否执行最后一次
利用一个定时器来控制,当flag为false时,则创建一个定时器,在规定的时间间隔后执行函数,如此哪怕未到时间间隔也会执行最后一次函数,如果在定时器内的函数执行前,flag为true且触发了函数,则清除定时器,只执行当前事件的函数
function throttle(fn,interval,option){
var timer=null,flag=true,option=option||false//默认不执行最后一次触发的函数
function throttled(){
var _this=this,
_arguments=arguments
if(flag){
if(timer){
//清除定时器
clearTimeout(timer)
//用于下次在等待时间内再触发时,创建定时器
timer=null
}
flag=false
fn.apply(_this,_arguments)
setTimeout(()=>{
flag=true
},interval)
}
//设置执行最后一次
else if(!timer&&option){
timer=setTimeout(()=>{
flag=false
fn.apply(_fn,_arugments)
timer=null
setTimeout(()=>{
flag=true
},interval)
},interval)
}
}
throttled.cancel=function(){
if(timer){
clearTimeout(timer)
timer=null
}
}
return throttled
}
实现2:采用时间戳
var throttle = function(fn,interval){
var pre = 0//上次执行的时间
var throttled = function(){
var _this = this
var _arguments = arguments
var now = new Date().getTime()
if(now-pre>=interval){
fn.apply(_this,_arguments)
pre=now
}
}
return throttled
}
优化是否执行最后一次执行
采用定时器实现,如果需要最后一次执行,则在第一次触发事件时创建定时器,如果最后一次触发事件时与上次执行函数的时间不满足要求,则不会执行函数,最终执行定时器中的回调函数
var throttle = function(fn,interval,option){
var pre = 0,timer=null,//定时器
option=option||false//默认不执行最后一次
var throttled = function(){
var _this = this
var _arguments = arguments
var now = new Date().getTime()
if(now-pre>=interval){
//清除定时器
if(timer){
clearTimeout(timer)
timer=null
}
fn.apply(_this,_arguments)
pre=now
}
/*第一次触发事件或者在定时器被清除后再触发事件,创建定时器,
确保当最后一次触发事件与最后一次执行函数的时间间隔<interval时,
仍会再执行一次函数*/
else if(!timer&&option){
timer=setTimeout(function(){
timer=null
fn.apply(_this,_arguments)
pre=now
},interval)
}
}
throttled.cancel=function(){
if(timer){
clearTimeout(timer)
timer=null
}
}
return throttled
}
以上的代码我都简单的测试过了,第一次在社区写文章,如果有疑问或者有错误欢迎指出呀~我代码主要是来自参考的文章,该文章写得比较清楚,而且还有优化返回值的,不过我还没来得及消化,下次再补充。
参考:codewhy公众号的文章《javascript防抖和节流》