什么是节流和去抖?
1、节流
那在JS里图片懒加载监听页面的scoll事件,或者监听鼠标的mousemove事件,会被浏览器频繁的触发造成浏览器过载,所以减少触发频率
2、去抖
用户多次连续点击按钮,函数毫秒内多次重复查询;短时间内的多次重复执行只执行最后一次。
JS实现方式
1、节流:
/** 实现思路:
** 参数需要一个执行的频率(wait),和一个对应的处理函数(func),
** 内部需要一个lastTime 变量记录上一次执行的时间
**/
function throttle(func, wait) {
let lastTime = null;// 为了避免每次调用lastTime都被清空,利用js的闭包返回一个function;此外声明为全局变量也可以
return function() {
let now = new Date();
// 如果上次执行的时间和这次触发的时间大于一个执行周期,则执行
if (now - lastTime - wait > 0) {
func();
lastTime = now;
}
}
};
// 由于闭包的存在,调用会不一样 这里设置时间间隔 为 400ms
let throttleRun = throttle(() => {
console.log(123);
}, 400);
window.addEventListener('scroll', throttleRun);
但是到这里,我们的节流方法是不完善的,因为我们的方法没有获取事件发生时的this对象,而且由于我们的方法简单粗暴的通过判断这次触发的时间和上次执行时间的间隔来决定是否执行回调,这样就会造成最后一次触发无法执行,或者用户出发的间隔确实很短,也无法执行,造成了误杀,所以需要对方法进行完善。
function throttle(func, wait) {
let lastTime = null;
let timeout;
return function() {
let context = this;
let now = new Date();
// 如果上次执行的时间和这次触发的时间大于一个执行周期,则执行
if (now - lastTime - wait > 0) {
// 如果之前有了定时任务则清除
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
func.apply(context, arguments);
lastTime = now;
} else if (!timeout) {
timeout = setTimeout(() => {
// 改变执行上下文环境
func.apply(context, arguments);
}, wait);
}
};
}
这样我们的方法就相对完善了,调用方法和之前相同。
2、去抖:
function debounce(func, wait) {
let lastTime = null;
let timeout;
return function() {
let context = this;
let now = new Date();
// 判定不是一次抖动
if (now - lastTime - wait > 0) {
setTimeout(() => {
func.apply(context, arguments);
}, wait);
} else {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
timeout = setTimeout(() => {
func.apply(context, arguments);
}, wait);
}
// 注意这里lastTime是上次的触发时间
lastTime = now;
}
}
3、分享:
方法一:
这种实现方式的思路很好理解:设置一个一间隔时间,比如50毫秒,以此时间为基准设置定时器,当第一次触发事件到第二次触发事件间隔小于50毫秒时,清除这个定时器,并设置一个新的定时器,以此类推,直到有一次事件触发后50毫秒内没有重复触发。这种设计方式有一个问题:本来应该多次触发的事件,可能最终只会发生一次。具体来说,一个循序渐进的滚动事件,如果用户滚动太快速,或者程序设置的函数节流间隔时间太长,那么最终滚动事件会呈现为一个很突然的跳跃事件,中间过程都被节流截掉了。这个例子举的有点夸张了,不过使用这种方式进行节流最终是会明显感受到程序比不节流的时候“更突兀”,这对于用户体验是很差的。有一种弥补这种缺陷的设计思路。
function debounce(method) {
clearTimeout(method.timer);
method.timer = setTimeout(function() {
method();
}, 50);
}
方法二:
第二种实现方式的思路与第一种稍有差别:设置一个间隔时间,比如50毫秒,以此时间为基准稳定分隔事件触发情况,也就是说100毫秒内连续触发多次事件,也只会按照50毫秒一次稳定分隔执行。代码如下:
var oldTime = new Date().getTime();
var delay = 50;
function throttle1(method) {
var curTime = new Date().getTime();
if (curTime - oldTime >= delay) {
oldTime = curTime;
method();
}
};
// 优化后解决方案
var timer = undefined,
delay = 50;
function throttle2(method) {
if (timer) {
return;
}
method();
timer = setTimeout(function() {
timer = undefined;
}, delay);
}