点击打开链接
DOM操作比起非DOM交互需要更多的内存和CPU时间,连续尝试进行过多的DOM相关操作会导致浏览器挂起,有时甚至会奔溃。函数节流是指一定时间内js方法只跑一次。某些代码不可以在没有间断的情况连续重复执行,第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。第二次调用该函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作就没有意义。如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器,目的是只有在执行函数的请求停止了一段时间之后才执行。该模式的基本形式:
var processor={
timeoutId:null,
//实际进行处理的方法
performProcessing:function(){
//实际执行的代码
},
//初始处理调用的方法
process:function(){
clearTimeout(this.timeoutId);//清除存好的timeoutId来阻止之前的调用被执行
var that=this;
this.timeoutId=setTimeout(function(){//创建一个新的定时器调用performProcessing()
that.performProcessing();
},100);
}
};
//尝试开始执行
processor.process();
时间间隔设为100ms,表示最后一次调用process()之后至少100ms才会调用performProcessing()。所以如果100ms之内调用了process()共20次,performProcessing()仍只会被调用一次。这个模式可以用throttle()函数来简化,这个函数可以自动进行定时器的设置和清除:
function throttle(method,context){//接收两个参数:要执行的函数,在哪个作用域执行
clearTimeout(method.tId);//定时器ID是存储在函数的tId属性中的
method.tId=setTimeout(function(){
method.call(context);
},100);
}
节流在resize事件中是最常用的,例如,假设一个
元素需要保持高度始终等同于宽度:window.οnresize=function(){
var div=document.getElementById("myDiv");
div.style.height=div.offsetWidth+"px";
};
首先,要计算offsetWidth属性,如果该元素或页面上其他元素有非常复杂的CSS样式,那么这个过程将会很复杂。其次,设置某个元素的高度需要对页面进行回流来令改动生效。如果页面有很多元素同时应用了相当数量的CSS,又需要很多计算。利用throttle()函数:
function resizeDiv(){
var div=document.getElementById("myDiv");
div.style.height=div.offsetWidth+"px";
}
window.οnresize=function(){
throttle(resizeDiv);//传入throttle函数而不是直接调用
};
在没有使用函数节流的情况下,为input绑定keyup事件处理函数:
function queryData(text){
console.log("搜索:"+text);
}
var input=document.getElementById("search");
input.addEventListener("keyup",function(event){queryData(this.value);
});
在这种情况下,每按下一个键盘键,就输出了一次。使用函数节流模式:
input.addEventListener("keyup",function(event){
throttle(queryData,null,500,this.value);
});
function throttle(fn,context,delay,text){
clearTimeout(fn.timeoutId);
fn.timeoutId=setTimeout(function(){
fn.call(context,text);
},delay);
}
函数节流增强版:
input.addEventListener("keyup",function(event){
throttle(queryData,null,500,this.value,1000);
});
function throttle(fn,context,delay,text,mustApplyTime){
clearTimeout(fn.timer);
fn._cur=Data.now();//记录当前时间
if(!fn._start){//若该函数是第一次调用,则直接设置_start,即开始时间
fn._start=fn._cur;
}
if(fn._cur-fn._start>mustApplyTime){//当期时间与上一次函数被执行的时间作差
fn.call(context,text);//必须执行一次
fn._start=fn._cur;
}else{//重新设置计时器
fn.timer=setTimeout(function(){
fn.call(context,text);
},delay);
}
}
函数防抖是指频繁触发的情况下,只要有足够的空闲时间,才执行代码一次。
var timer=false;
document.getElementById("debounce").οnscrοll=function(){
clearTimeout(timer);//如果多次触发,把上次的延迟执行代码清除,重新开始
timer=setTimeout(function(){
console.log("函数防抖");
},300);//如果计时完毕,没有方法进来访问触发,则执行代码。
};
函数防抖的实现重点是巧用setTimeout做缓存池,而且可以轻易地清除待执行的代码。