函数防抖(debounce)和函数节流(throttle)
相同:为了缓解函数频繁调用、大量计算导致页面卡顿
不同:防抖是将多次执行变为最后一次执行,节流是将多次执行变为在规定时间内只执行一次.
如上图,一条竖线代表一次函数调用。
防抖
定义:
指触发事件后在规定时间内回调函数只能执行一次,如果在规定时间内又触发了该事件,则会重新开始算规定时间。
两种版本
防抖分为两种:
- 1)非立即执行版:事件触发->延时->执行回调函数;如果在延时中,继续触发事件,则会重新进行延时.在延时结束后执行回调函数.常见例子:就是input搜索框,客户输完过一会就会自动搜索
- 2)立即执行版:事件触发->执行回调函数->延时;如果在延时中,继续触发事件,则会重新进行延时.在延时结束后,并不会执行回调函数.常见例子:就是对于按钮防点击.例如点赞,心标,收藏等有立即反馈的按钮.
// 防抖:非立即执行版
function debounce (fn,delay){
return function(){
let arg = arguments,context = this;
clearTimeout(fn.timer);
fn.timer = setTimeout(function(){
fn.apply(context,arg);
},delay);
}
}
//带有立即执行选项的防抖函数:
//思路和上面的大致相同,如果是立即执行,则定时器中不再包含回调函数,而是在回调函数执行后,仅起到延时和重置定时器标识的作用
function debounce(fun, delay = 500,immediate = true) {
let timer = null //保存定时器
return function (args) {
let that = this
let _args = args
if (timer) clearTimeout(timer); //不管是否立即执行都需要首先清空定时器
if (immediate) {
if ( !timer) fun.apply(that, _args) //如果定时器不存在,则说明延时已过,可以立即执行函数
//不管上一个延时是否完成,都需要重置定时器
timer = setTimeout(function(){
timer = null; //到时间后,定时器自动设为null,不仅方便判断定时器状态还能避免内存泄露
}, delay)
}
else {
//如果是非立即执行版,则重新设定定时器,并将回调函数放入其中
timer = setTimeout(function(){
fun.call(that, _args)
}, delay);
}
}
}
节流
定义:
当持续触发事件时,在规定时间段内只能调用一次回调函数。如果在规定时间内又触发了该事件,则什么也不做,也不会重置定时器.
与防抖比较:
防抖是将多次执行变为最后一次执行,节流是将多次执行变为在规定时间内只执行一次.一般不会重置定时器. 即不会if (timer) clearTimeout(timer);
(时间戳+定时器版除外)
应用场景:
两个条件:
1,客户连续频繁地触发事件
2,客户不再只关心"最后一次"操作后的结果反馈.而是在操作过程中持续的反馈.
例如:
- 鼠标不断点击触发,点击事件在规定时间内只触发一次(单位时间内只触发一次)
- 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
//时间戳版:
//这里fun指的就是回调函数,我就不写出来了
function throttle(fun, delay = 500) {
let previous = 0; //记录上一次触发的时间戳.这里初始设为0,是为了确保第一次触发产生回调
return function(args) {
let now = Date.now(); //记录此刻触发时的时间戳
let that = this;
let _args = args;
if (now - previous > delay) { //如果时间差大于规定时间,则触发
fun.apply(that, _args);
previous = now;
}
}
}
//定时器版:
function throttle(fun, delay = 500) {
let timer;
return function(args) {
let that = this;
let _args = args;
if (!timer) { //如果定时器不存在,则设置新的定时器,到时后,才执行回调,并将定时器设为null
timer = setTimeout(function(){
timer = null;
fun.apply(that, _args)
}, delay)
}
}
}
//时间戳+定时器版: 实现第一次触发可以立即响应,结束触发后也能有响应 (该版才是最符合实际工作需求)
//该版主体思路还是时间戳版,定时器的作用仅仅是执行最后一次回调
function throttle(fun, delay = 500) {
let timer = null;
let previous = 0;
return function(args) {
let now = Date.now();
let remaining = delay - (now - previous); //距离规定时间,还剩多少时间
let that = this;
let _args = args;
clearTimeout(timer); //清除之前设置的定时器
if (remaining <= 0) {
fun.apply(that, _args);
previous = Date.now();
} else {
timer = setTimeout(fun, remaining); //因为上面添加的clearTimeout.实际这个定时器只有最后一次才会执行
}
}
}
参考: