这一篇文章我想写一下防抖与节流,因为我自己不是很理解而且说实话,以前知道,但是老忘,虽然概念还有一些简单的写法会,但还是缺乏练习和深刻的理解。

<div id="app"></div>
function fn(e) {
console.log(this);
console.log(e);
app.innerHTML = num ++;
}
1.防抖
- 简单实现
function debounce(fn, delay) {
let timer = null;
return function(){
clearTimeout(timer);
timer = setTimeout(function () {
fn();
}, delay);
}
}
使用节流后
app.onmousemove = fdebounce(fn, 1000);

我们来看一下this和event。

- 修复this指向和事件参数
function debounce(fn, delay) {
let timer = null,
that;
return function(e){
that = this;
clearTimeout(timer);
timer = setTimeout(function () { //this指向window(非严格模式)
fn.apply(that, [e]);
}, delay);
}
}
或者是
function debounce(fn, delay) {
let timer = null;
return function(e){
clearTimeout(timer);
timer = setTimeout(()=>{ //箭头函数
fn.apply(this, [e]);
}, delay);
}
}

- 增强版(是否立即执行)
function debounce(fn, delay, immediate) {
let timer = null,
that;
return function (e) {
that = this;
clearTimeout(timer);
if(immediate){ //是否立即执行
if(!timer){ // 如果没有设置定时器就先执行
fn.apply(that, [e]);
}
timer = setTimeout(function () {
timer = null;// 设置定时器,使其在delay毫秒内不能执行,过了delay毫秒后在执行
}, delay);
}else{ //否则延时执行
timer = setTimeout(function () {
fn.apply(that, [e])
}, delay);
}
};
}
//这个if...else只能执行一个,要不先执行,要不延时执行

- 节流
- 简易版(时间戳)
function throttle(fn, delay) {
let last = 0, //上次执行时间
now; //执行时的时间
return function (e) {
now = Date.now(); //当前时间
if(now - last >= delay){ //如果两次时间间隔大于delay,就执行
last = now; //重新赋值
fn();
}
};
}

- setTimeout版本(修复this指向和事件参数)
function throttle(fn, delay) {
let that,
timer = null;
return function (e) {
that = this;
if(!timer){ //如果没设置定时器就执行
timer = setTimeout(function () {
fn.apply(that, [e]);
timer = null; //果delay毫秒就设置timer,这样就能delay执行一次
}, delay);
}
};
}
- 修复this指向和事件参数(时间戳)
function throttle(fn, delay) {
let last = 0, //上次执行时间
now; //执行时的时间
return function (e) {
now = Date.now(); //当前时间
if(now - last >= delay){ //如果两次时间间隔大于delay,就执行
last = now; //重新赋值
fn.apply(this, [e]);
}
};
}
- 区别
- 时间戳版本的会先执行
- setTimeout版本的会后执行
所以可以结合一下他们两个。
- 结合版
function throttle(fn, delay) {
let last = 0, //上次执行时间
now, //当前时间
leftTime, //剩余时间
that,
timer = null;
return function (e) {
that = this;
now = Date.now();
leftTime = delay - (now - last);
if(leftTime <= 0){ //保证一开始就执行(先执行)
last = now;
fn.apply(that, [e]);
}else{
timer = setTimeout(function() { //延时执行
fn.apply(that, [e]);
timer = null;
},delay)
}
};
}
这样做总体思路没错,但是第一次会执行以后就是两个一起执行,因为条件都满足。
- 修改
function throttle(fn, delay) {
let last = 0,
now,
leftTime,
that,
timer = null;
return function (e) {
that = this;
now = Date.now();
leftTime = delay - (now - last);
if(leftTime <= 0){
if(timer){ //如果有定时器就清除
clearTimeout(timer);
timer = null;
}
last = now;
fn.apply(that, [e]);
}else if(!timer){
timer = setTimeout(function() {
last = now; //如果时间满足就让他不满足
//总之除了第一次就只让其中一个执行
fn.apply(that, [e]);
timer = null;
},delay)
}
};
}
