一开始,我有点混淆防抖和节流,乍一看好像是一样的,都是限制提交次数嘛。
但是,等待机制不太一样。同样是2s的等待周期,防抖是只要触发间隔在2s以内,它就重新计时;节流是,2s给予一次执行时间函数机会,还能设置第一次和最后一次的操作是否执行。
throttle(fn, wait, { leading:true; trailing:false })(默认)
- 默认情况
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#div1{
display: flex;
justify-content: center;
align-items: center;
height: 200px;
background-color: pink;
color: grey;
}
#div2{
display: flex;
justify-content: center;
align-items: center;
height: 200px;
background-color: lightcoral;
color: #ffffff;
}
</style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<script>
// leading:true; trailing:false
function throttle(fn,wait){
let _, args;
let old = 0;
return function(){
_ = this;
console.log('我被调用了!');
args = arguments;
let now = new Date().valueOf(); //获取时间戳,一个类似1586009260828的序列
if(now - old > wait){
console.log('now这被调用了');
fn.apply(_, args);
old = now;
}
}
}
let count = 0;
let num = 0;
let odiv1 = document.querySelector('#div1');
let odiv2 = document.querySelector('#div2');
function doSomething(ev){
// console.log(ev); //优化前是undefined
odiv1.innerHTML = count++;
// console.log(this); //odiv
}
setInterval(function () {
odiv2.innerHTML = num++;
},1000);
odiv1.onmousemove = throttle(doSomething, 10000);
</script>
</body>
看下图,时间戳成功捕获第一次trigger操作,if条件成立,立马执行了一次(对应leading:true);往后再触发需要等待wait时长;等待时长在第2次以后的每次‘我被调用了’这里
2. { leading:false; trailing:true }
function throttle(fn,wait){
let _, args, timer;
clearTimeout(timer);
return function(){
_ = this;
args = arguments;
if(!timer){
timer = setTimeout(()=>{
fn.apply(_, args);
timer = null;
}, wait);
}
}
}
这个定时器和防抖的挺像的,每次触发,都要等wait时长才执行,但执行完后不用等!就能接受下次触发。等待时长在(return,timer)之间。
- { leading:true; trailing:true }
function throttle(fn,wait){
let _, args, timer;
let old = 0;
return function(){
_ = this;
console.log('我被调用了!');
args = arguments;
let now = new Date().valueOf(); //获取时间戳,一个类似1586009260828的序列
if(now - old > wait){
if(timer){
clearTimeout(timer);
timer = null;
}
console.log('now这被调用了');
fn.apply(_, args);
old = now;
}
if(!timer) {
timer = setTimeout(()=>{
old = new Date().valueOf();
console.log('定时器被调用了');
fn.apply(_, args);
timer = null;
}, wait);
}
}
}
第一次触发时,前4步一定会执行;过后,如果在下一个wait到来前触发,则调用timer;否则,调用now->timer
三者合并:
function throttle(fn,wait,option){
let _, args, timer;
let old = 0;
if(!option) option = {};
return function(){
_ = this;
args = arguments;
let now = new Date().valueOf(); //获取时间戳,一个类似1586009260828的序列
if(option.leading === false && !old) old = now; // 不让第一次执行
if(now - old > wait)
{
// 第一次立即执行
if(timer){
clearTimeout(timer);
timer = null;
}
fn.apply(_, args);
old = now;
}
else if(!timer && option.trailing !== false)
{
// 最后一次会执行
timer = setTimeout(()=>{
old = new Date().valueOf();
fn.apply(_, args);
timer = null;
}, wait);
}
}
}