在前端开发的过程中经常会遇到这样的需求--通过鼠标的事件(mousemove,srcoll 等)连续触发目标函数,如果不与后台交互,在某些情况下还说的过去,但是一旦与后台交互,比如Ajax请求,那么这个时候会对后台造成很大的压力,那么如何才能避免这种状况呢?这里就不得不请出今天要说的主角--函数的防抖和节流。
函数防抖与函数节流的区别:
函数节流: 不是让函数不执行,而是让函数在一定的时间内执行的次数减少一些
函数防抖: 是超过一定的时间内执行一次,比如在mousemove事件中,鼠标停止移动后的1s后才执行。
如果还不明白,可以移步的这里自己体验一把吧,哈哈!函数防抖和节流体验
函数防抖
先看一个例子,效果图(从参考文章上扒下来的,说明问题就好)
var num = 1;
var content = document.querySelector('.container');
addNum ();
function addPlus() {
num++;
content.innerHTML = num
}
document.onmousemove = addNum
函数防抖前的样子,可以想象如果addPlus里面执行的不是简单的计算,而是向服务器发送请求,可以预见服务器要承担多大的压力。
如果使用防抖函数呢? 且看如下函数和效果图
document.onmousemove = debounce(addPlus,1000)
// 一般的防抖函数
function debounce(doSomething,wait){
var timeout;//需要一个外部变量,为增强封装,所以使用闭包
return function(){
var _this = this,
_arguments = arguments;
// 关键,每次执行前都要先清空上一个timeOut,由于速度特别快,
// 所以在移动鼠标的时候,里面的函数不会执行,直到当鼠标停止的时候,此时生成最后一个定时器
// 并1秒后调用doSomething
clearTimeout(timeout);
// 再生成一个Timeout
timeout = setTimeout(function(){
doSomething.apply(_this,_arguments);
},wait);
}
};
立即执行版
function debounceIm(doSomething,wait,isImmediate){
var timeout;
return function(){
var _this = this,
_arguments = arguments;
clearTimeout(timeout);
if(isImmediate){
var isTrigger = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait)
isTrigger&&doSomething.apply(_this,_arguments);
}else{
timeout = setTimeout(function(){
doSomething.apply(_this,_arguments);
},wait);
}
}
}
document.onmousemove = debounceIm(addPlus,1000,true);
上述函数解析:
如果isImmediate=true:
当鼠标第一次开始移动的时候,timeout是undefined,所以isTrigger是true,所以就会立即执行isTrigger&&doSomething.apply(_this,_arguments);并且timeout已经是一个对象了,此时如果鼠标不停的移动就会导致isTrigger是false,延时器一直被清空,直到停下来的时候过wait后执行timeout=null。
如果isImmediate=false:
就不说啦,跟第一次总结的一样。
上述的效果图如下:
函数节流
截留的目的不是不执行,而是让函数执行的频率变低,通常有两种方案,一种是使用时间戳,一种是使用定时器
时间戳版
function trottle(doSomeThing,wait) {
var _this,
_arguments,
initTime = 0;
return function(){
var now = +new Date();
_this = this;
_arguments = arguments;
// 当间隔时间大于一定的时间后才触发对应的函数
if(now - initTime>wait){
doSomeThing.apply(_this,_arguments);
initTime = now;
}
}
}
document.onmousemove = trottle(addPlus,2000)
第一次mousemove的时候,会立即执行一次(因为第一次mousemove的时候now - initTime很大),之后就看时间间隔了
定时器(主要思想是通过mousemove不停创建新的延时器)
function throttle(doSomething,wait){
var timeout;
return function(){
var _this = this;
_arguments = arguments;
if(!timeout){
timeout = setTimeout(function(){
// 主要是让每次move的时候,使!timeout为true
timeout = null;
doSomething.apply(_this,_arguments);
},wait);
};
}
}
document.onmousemove = throttle(addPlus,2000)
这个方法的执行过程是这样的:
第一次mousemove由于timeout是undefined,所以会进入判断并创建一个延时器,此时如果我们立即不断地mousemove,!time为false,就不会进入判断,直到间隔wait时间段后,timeout为null,此后再mousemove就会创建新的延时器,以此类推不断地循环...
总结
这篇文章主要记录一下自己对参考文章的思考过程,其中可能会有疏漏,还望大家指正~