如何解决js函数防抖、节流出现的问题
React中使用防抖函数和节流函数
在React事件调用时,React传递给事件处理程序是一个合成事件对象的实例。SyntheticEvent对象是通过合并得到的。这意味着在事件回调被调用后,SyntheticEvent对象将被重用并且所有属性都将被取消。这是出于性能原因。因此,您无法以异步方式访问该事件。React合成事件官方文档
所以在用防抖或节流函数封装时,异步方式访问事件对象出现问题。解决的方法如下:
方法一:调用合成事件对象的persist()方法event.persist&&event.persist()//保留对事件的引用
方法二:深拷贝事件对象constevent=e&&{...e}//深拷贝事件对象
functiondebounce(func,wait=500){
lettimeout;//定时器变量
returnfunction(event){
clearTimeout(timeout);//每次触发时先清除上一次的定时器,然后重新计时
event.persist&&event.persist()//保留对事件的引用
//constevent=e&&{...e}//深拷贝事件对象
timeout=setTimeout(()=>{
func(event)
},wait);//指定xxms后触发真正想进行的操作handler
};
}
防抖debounce
防抖Debounce多次触发,只在最后一次触发时,执行目标函数。
函数防抖就是,延迟一段时间再执行函数,如果这段时间内又触发了该函数,则延迟重新计算。
应用场景
(1)通过监听某些事件完成对应的需求,比如:
通过监听scroll事件,检测滚动位置,根据滚动位置显示返回顶部按钮
通过监听resize事件,对某些自适应页面调整DOM的渲染(通过CSS实现的自适应不再此范围内)
通过监听keyup事件,监听文字输入并调用接口进行模糊匹配
(2)其他场景
表单组件输入内容验证
防止多次点击导致表单多次提交
简单实现
functiondebounce(fn,wait){
lett
return()=>{
letcontext=this
letargs=arguments
if(t)clearTimeout(t)
t=setTimeout(()=>{
fn.apply(context,args)
},wait)
}
}
完整实现
functiondebounce(func,wait,immediate){
lettime;
letdebounced=function(){
letcontext=this;
if(time)clearTimeout(time);
if(immediate){
letcallNow=!time;
if(callNow)func.apply(context,arguments);
time=setTimeout(
()=>{time=null}//见注解
,wait)
}else{
time=setTimeout(
()=>{func.apply(context,arguments)}
,wait)
}
};
debounced.cancel=function(){
clearTimeout(time);
time=null
};
returndebounced
}
//underscore.jsdebounce
//
//Returnsafunction,that,aslongasitcontinuestobeinvoked,willnot
//betriggered.Thefunctionwillbecalledafteritstopsbeingcalledfor
//Nmilliseconds.If`immediate`ispassed,triggerthefunctiononthe
//leadingedge,insteadofthetrailing.
_.debounce=function(func,wait,immediate){
vartimeout,args,context,timestamp,result;
//处理时间
varlater=function(){
varlast=_.now()-timestamp;
if(last=0){
timeout=setTimeout(later,wait-last);//10ms6ms4ms
}else{
timeout=null;
if(!immediate){
result=func.apply(context,args);
if(!timeout)context=args=null;
}
}
};
react中调用方法
this.handleGetCustomerNameList=debounce(this.handleGetCustomerNameList.bind(this),500);
节流throttle
节流:函数间隔一段时间后才能再触发,避免某些函数触发频率过高,比如滚动条滚动事件触发的函数。
###简单实现
functionthrottle(fn,wait,mustRun){
letstart=newDate()
lettimeout
return()=>{
//在返回的函数内部保留上下文和参数
letcontext=this
letargs=arguments
letcurrent=newDate()
clearTimeout(timeout)
letremaining=current-start
//达到了指定触发时间,触发该函数
if(remaining>mustRun){
fn.apply(context,args)
start=current
}else{
//否则wait时间后触发,闭包保留一个timeout实例
timeout=setTimeout(fn,wait);
}
}
}
完整实现
functionthrottle(func,wait,options){
lettime,context,args,result;
letprevious=0;
if(!options)options={};
letlater=function(){
previous=options.leading===false?0:newDate().getTime();
time=null;
func.apply(context,args);
if(!time)context=args=null;
};
letthrottled=function(){
letnow=newDate().getTime();
if(!previous&&options.leading===false)previous=now;
letremaining=wait-(now-previous);
context=this;
args=arguments;
if(remaining<=0||remaining>wait){
if(time){
clearTimeout(time);
time=null;
}
previous=now;
func.apply(context,args);
if(!time)context=args=null;
}elseif(!time&&options.trailing!==false){
time=setTimeout(later,remaining);
}
};
returnthrottled;
}
//underscore.jsthrottle
//Returnsafunction,that,wheninvoked,willonlybetriggeredatmostonce
//duringagivenwindowoftime.Normally,thethrottledfunctionwillrun
//asmuchasitcan,withoutevergoingmorethanonceper`wait`duration;
//butifyou'dliketodisabletheexecutionontheleadingedge,pass
//`{leading:false}`.Todisableexecutiononthetrailingedge,ditto.
_.throttle=function(func,wait,options){
varcontext,args,result;
vartimeout=null;
varprevious=0;
if(!options)options={};
varlater=function(){
previous=options.leading===false?0:_.now();
timeout=null;
result=func.apply(context,args);
if(!timeout)context=args=null;
};
returnfunction(){
varnow=_.now();
if(!previous&&options.leading===false)previous=now;
varremaining=wait-(now-previous);
context=this;
args=arguments;
if(remaining<=0||remaining>wait){
if(timeout){
clearTimeout(timeout);
timeout=null;
}
previous=now;
result=func.apply(context,args);
if(!timeout)context=args=null;
}elseif(!timeout&&options.trailing!==false){
timeout=setTimeout(later,remaining);
}
returnresult;
};
};
react中调用方法
this.handleGetCustomerNameList=throttle(this.handleGetCustomerNameList.bind(this),500);
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。