防抖
效果:短时间内一直触发事件时,只执行最后一次触发的函数
代码:借助setTimeout
来实现,当下次触发事件时,如果setTimeout
已经存在,则清除setTimeout
并重新计算时间
/*
fn: 要执行的函数
delay: 延迟执行时间ms
args: fn函数的参数(数组)
context: fn函数的this指向(默认是函数执行时的上下文环境)
*/
function debounce(fn, delay, args, context) {
let timer = null;
return function() {
context = context || this;
args = args || arguments;
if(timer != null) {
clearTimeout(timer);
}
timer = setTimeout(function() {
fn.apply(context, args);
}, delay)
}
}
function handle() {
console.log('hahandy');
}
window.onscroll = debounce(handle, 1000);
节流
效果:短时间内一直触发事件,在触发时间内每隔一段时间执行一次触发的函数
代码:借助标记位
和setTimeout
,在setTimeout
开始计算时间后,将标记位
设置为false
,等待函数执行,当函数执行后,将标记位
设置为true
,进行下一次的setTimeout
/*
fn: 要执行的函数
delay: 延迟执行时间ms
args: fn函数的参数(数组)
context: fn函数的this指向(默认是函数执行时的上下文环境)
*/
function throttle(fn, delay, args, context) {
let timer = null;
return function() {
context = context || this;
args = args || arguments;
if(!timer) {
timer = setTimeout(function() {
fn.apply(context, args);
timer = null;
}, delay)
}
}
}
function handle() {
console.log('hahandy');
}
window.onscroll = throttle(handle, 1000);
如果有另外的需求,比如说首次触发要执行
- 第一种可以考虑使用
时间戳
,当时间间隔大于delay
时(相当于首次触发,所以这种方法适合delay值不是特别大的情况。。。),直接触发fn
function throttle(fn, delay, args, context) {
let timer = null;
let startTime = Date.now();
return function() {
context = context || this;
args = args || arguments;
let currentTime = Date.now();
if(currentTime - startTime >= delay) {
fn.apply(context, args);
}else if(!timer) {
timer = setTimeout(function() {
fn.apply(context, args);
timer = null;
}, delay)
}
startTime = Date.now();
}
}
- 第二种方法我个人更喜欢,几乎完美,在大佬的帮助下有了这样的实现方式,设置一个
标记位flag
用来记录当前是否有timer
,如果没有,则执行fn
,显然,首次触发时,timer
为null
,flag
为true
,fn
执行,然后,由计时器来控制何时将timer
设为null
,fn
才执行
function throttle(fn, delay, args, context) {
let timer = null;
return function() {
context = context || this;
args = args || arguments;
let flag = !timer;
if(!timer) {
timer = setTimeout(function() {
timer = null;
}, delay)
}
if(flag) {
fn.apply(context, args);
}
}
}
小结
- 无论是防抖还是节流,都属于
性能优化
的一种,究竟使用哪种方案,还是要看具体的需求。 - 如果是页面可能会一直触发事件,但只希望执行一次,比如某些按钮的点击事件,可以考虑防抖(防止手抖点多了)。
- 如果是页面可能会一直触发事件,但希望间隔一段时间执行一次,比如某些滚动事件,可以考虑节流(高效减少触发次数)。
- 如果有别的特殊需求,根据需要改写即可。