1.节流的原理:
🌵节流:如果你持续触发事件,每隔一段时间,只执行一次。
例子🌰是通过underscore的throttle实现的小例子:
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#container{
width: 100%;
height: 200px;
line-height: 200px;
text-align: center;
background: pink;
color:rebeccapurple;
}
</style>
</head>
<body>
<div id="container"></div>
<!-- <button id="btn">取消防抖</button> -->
<script src="https://cdn.bootcdn.net/ajax/libs/underscore.js/1.10.2/underscore-min.js"></script>
<script src="./throttle.js"></script>
</body>
</html>
throttle.js文件:
let count=0;
let container=document.querySelector("#container");
function dosomething(){
container.innerHTML=count++;
};
let doit= _.throttle(dosomething,2000,
{
leading:false,
trailing:true
});
container.onmousemove=doit;
2.使用时间戳来实现第一版节流函数:
"不顾头,顾尾":实现了首次触发事件的时候,立即执行,最后一次的离开的时候,不执行。
🍊使用underscore实现:
//进入的时候立即执行,离开的时候不执行
let doit= _.throttle(dosomething,2000,
{
leading:true,
trailing:false
});
🍊使用时间戳来实现:
function throttle(func,wait){
let context,args;
let old=0;
return function(){
//获取现在的时间戳
let now=new Date().valueOf();
context=this;
if(now-old>wait){
func.apply(context,args);
//操作执行完毕后,让old等于now
old=now;
};
}
}
let count=0;
let container=document.querySelector("#container");
function dosomething(){
container.innerHTML=count++;
};
//自己通过时间戳实现的节流
let dome=throttle(dosomething,2000);
container.onmousemove=dome;
注意的点:
- 时间戳的获得:new Date().valueOf();
- 再有就是underscore的throttle的第三个参数是一个对象:
- 参数对象:{leading:false,trailing:true};
- leading和trailing这两个参数同时为false的时候会出bug~!!!
3.使用定时器来完成节流函数:
"顾头,不顾尾"--- 进入的时候不立即执行,离开时还会执行一次
🍊使用underscore实现:
//进入的时候不立即执行,离开时还会执行一次
let doit1= _.throttle(dosomething,2000,
{
leading:false,
trailing:true
});
🍊使用定时器实现:
//定时器版本的节流函数
function throttleit(func,wait){
let context,args,timer;
return function(){
context=this;
args=arguments;
if(!timer){ //对timer进行取反
timer=setTimeout(function(){
func.apply(context,args);
timer=null; //事件执行完了之后需要对timer计时器进行清理
},wait)
}
}
};
4.时间戳和定时器双剑合并,实现节流函数:
🍊使用underscroe实现:
//进入的时候会立即执行,离开后还会再执行一次
let doit2= _.throttle(dosomething,2000,
{
leading:true,
trailing:true
});
🍊使用定时器和时间戳取长补短实现:
//定时器时间戳混合双打篇
function throttleDouble(func,wait){
let context,args,timer;
let old=0;
return function(){
context=this;
//获取当前函数的实参
args=arguments;
//获得时间戳
let now=new Date().valueOf();
//在首次进入的时候,能够保证立即执行。
if(now-old>wait){
if(timer){
clearTimeout(timer);
timer=null;
}
func.apply(context,args);
old=now;
};
//在后续的执行过程中完成每wait时间内,执行一次。
if(!timer){
timer=setTimeout(function(){
old=new Date().valueOf();
timer=null;
func.apply(context,args);
},wait)
}
};
}
🌵思路分析:
- 时间戳能够实现在首次进入的时候立即执行。
- 定时器能够实现每个wait时间内,执行一次。
5.函数节流完整优化:
🌵上面的2、3、4分成了三种情况:
第一次会执行,离开时不执行:
let doit= _.throttle(dosomething,2000, { leading:true, trailing:false });第一次不执行,离开时执行:
//进入的时候不立即执行,离开时还会执行一次 let doit1= _.throttle(dosomething,2000, { leading:false, trailing:true });第一次执行,离开时也执行:
let doit2= _.throttle(dosomething,2000, { leading:true, trailing:true });
🌵代码实现:
function throttleAll(func,wait,options){
let context,args,timer;
let old=0;
//如果没有设置第三个参数,那么第三个参数为空对象
if(!options) options={};
let later=function(){
old=new Date().valueOf();
timer=null;
func.apply(context,args);
}
return function(){
context=this;
//获取当前函数的实参
args=arguments;
//获得时间戳
let now=new Date().valueOf();
//
if(options.leading===false && !old){
old=now;
}
//在首次进入的时候,能够保证立即执行。
if(now-old>wait){
if(timer){
clearTimeout(timer);
timer=null;
}
func.apply(context,args);
old=now;
};
//在后续的执行过程中完成每wait时间内,执行一次。
if(!timer &&options.trailing!==false){
timer=setTimeout(later,wait)
}
};
}