一、函数的防抖和节流产生的原因
- 有一些浏览器事件可以在很短的事件内快速触发多次,例如 调整窗口大小 或 向下滚动 页面。例如,如果将事件监听器绑定到窗口滚动事件上,并且用户继续非常快速地向下滚动页面,你的事件可能会在3秒的范围内被触发数千次。这可能会导致一些严重的性能问题
- 在一些事件中,如滚动、窗口调整大小,或键盘按下的事件时,一定要提及函数防抖动和函数节流来提升页面速度和性能
- 直接绑定函数到scroll事件是非常错误的决定,当用户滚动页面时,页面可能会变得非常慢甚至未响应。而函数防抖和函数节流是解决这个问题的一种方式,通过限制需要经过的事件,直至再次调用函数,在处理一些高频率触发的 DOM 事件的时候,它们都能极大提高用户体验
- 为了提高页面性能,有时需要对高频率触发的事件(scrllo, resize, mousemove, touchmove)进行防抖(Debounce)或者节流(Throttle)处理
二、函数的防抖
- 如果一个事件被频繁触发多次,并且触发的时间间隔过短,则防抖函数可以使得对应的事件处理函数只执行一次
- 把一系列连续的事件,只处理一次,即只调用一次事件处理程序,强调一系列连续触发的事件
- 一系列事件:
1)在指定时间间隔(距离上次触发的时间)内触发的相同类型事件视为一个系列的事件
2)举例:假如scroll事件分别在0,1,3,7,8时间触发了事件A,B,C,D,E。假如规定时间间隔为2,则A,B,C属于一个系列(D,C之间间隔为3);D,E属于一个系列。防抖的目的就是把A,B,C构成的系列(或者D,E构成的系列)事件合并成一个,即只执行A或者C(D或者E) - 代码如下:
// debounce函数用来包裹我们的事件处理方法
function debounce(fn, delay){
// 持久化一个定时器
let timer = null
// 闭包函数可以访问timer
return function(){
// 通过 this 和 arguments 获得函数的作用域和参数
let context = this
let args = arguments
// 如果事件被触发,清除timer并重新开始计时
clearTimeout(timer)
timer = setTimeout(function() {
fn.apply(context, args)
}, delay)
}
}
function foo(){
console.log('You are scrolling!')
}
document.addEventListener('scroll', debounce(foo, 50));
- 防抖的简单实现:
1)代码如下:
var debounce = function(func, delay){
var timeHandle = null;
return function(){
var context = this,
args = arguments,
later = function(){
func.apply(context, arguments);
};
if(timeHandle) {
clearTimeout(timeHandle); // 覆盖上次事件回调:清除上次未执行的回调,新建个回调执行定时器
}
timeHandle = setTimeout(later, delay);
}
}
2)图示说明:事件处理程序在一系列事件触发后delay时间后执行
三、函数的节流
- throttle 的概念理解稍微容易一些,如果一个事件被频繁触发多次,节流函数可以按照固定频率去执行对应的事件处理方法
- 一段时间内的事件,只处理一次,即只调用一次事件处理程序。强调一段时间内
- 代码如下:
function throttle(fn, threshold){
var last
var timer
threshold || (threshold = 250)
return function(){
let context = this
let args = arguments
var now = +new Date()
if(last&&now<last+threshold){
clearTimeout(timer)
timer = setTimeout(function(){
last = now
fn.apply(context, args)
},threshold)
}else {
last = now
fn.apply(context, args)
}
}
}
- 节流的简单实现:
代码如下:
var throttle = function(func, delay){
var previous = 0;
return function(){
var context = this,
args = arguments,
curr = Date.now();
// 如果时间间隔超过delay时间,则执行回调
if(curr - previous >= delay){
previous = curr; // 更新previous
func.apply(context, args);
}
console.log(curr)
}
}