学习笔记,参考以下来源,侵删
定义:
- 防抖(debounce):短时间间隔地连续触发事件,只执行一次事件的回调函数(触发的时间间隔n<指定的延迟时间delay,则只执行一次)
- 节流(throttle):固定函数的执行速率;如果持续连续触发事件,则每隔指定的时间(wait)执行一次事件触发的函数
以下为可视化执行过程的网址,以便理解
应用场景:
防抖:
(设定延迟时间delay=300ms)
- 在mousemove、keydown等连续频繁的事件触发, 300ms没有再次触发事件才调用一次监听函数
- 表单的实时校验(避免频繁发送验证请求)
节流:
(设定时间间隔wait=100ms)
- window的resize、scroll的事件监听优化,避免频繁触发,确保在100ms内最多触发一次,
- mousemove事件统计鼠标移动轨迹,每100ms记录一次鼠标位置
实现:
防抖:
初步实现:
delay时间内,事件最后一次触发后执行一次
/**
* fn:回调函数
* delay:延迟时间
* */
function debounce({fn, delay=300}){
let timer = null
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
}, delay)
}
}
//调用
document.addEventListener('mousemove', debounce0({
fn:function(a){
console.log('防抖', this, arguments)
},
delay:800
}))
优化:
可设置触发后是否立即调用,delay秒之后才能再次调用
/**
* fn:回调函数
* delay:延迟时间
* immediately:触发后是否立即调用一次
* */
function debounce({fn, delay=300,immediately=true}){
let timer = null
return function(){
if(timer){
clearTimeout(timer)
}
if(immediately){
// timer为null则执行,否则不执行
if(!timer){
fn.apply(this, arguments)
}
// delay时间后再把timer置空(delay时间后才能再次触发)
timer = setTimeout(() => {
timer = null
}, delay)
}else{
// delay时间内最后一次触发执行
timer = setTimeout(() => {
fn.apply(this, arguments)
}, delay)
}
}
}
// 调用
document.addEventListener('mousemove', debounce({
fn:function(a){
console.log('防抖', this, arguments)
},
wait:1000
}))
节流
初步实现:
- 定时器实现:
-
- 触发后不会立即执行,在第一次触发wait时间之后才会第一次执行
-
- 最后一次触发的时间如果在两次执行时间之间,最后还会执行一次
function throttle({fn, wait=500}){
let timer = null
return function(){
if(!timer){
timer = setTimeout(() => {
fn.apply(this, arguments)
clearTimeout(timer)
timer = null
}, wait)
}
}
}
document.addEventListener('mousemove', throttle({
fn:function(a){
console.log('防抖', this, arguments)
},
wait:500
}))
// 调用
document.addEventListener('mousemove', throttle({
fn:function(a){
console.log('防抖', this, arguments)
},
wait:1000
}))
- 时间戳实现
-
- 首次触发立即执行一次
-
- 停止触发后,无论停止时间在哪,都不会再执行。例如,1 秒执行 1 次,在 4.2 秒停止,则第 5 秒不会再执行 1 次
function throttle({fn, wait=300}){
let previous = 0
return function(){
let now = +new Date()
// 当前时间距离上次执行时间间隔大于wait
if(now-previous > wait){
fn.apply(this, arguments)
// 记录此次执行的时间
previous = now
}
}
}
// 调用
document.addEventListener('mousemove', throttle({
fn:function(a){
console.log('防抖', this, arguments)
},
wait:1000
}))
- 结合定时器和时间戳实现
-
- 第一次触发立即执行
-
- 定时器用来设置在最后退出时增加一个延时执行
function throttle({fn, wait = 300}){
let previous = 0,
timer = null
return function(){
let now = +new Date()
// remaining以防客户端修改时间出现now < previous
let remaining = wait - (now - previous)
if(remaining <= 0 || remaining > wait){
fn.apply(this, arguments)
previous = now
if(timer){
clearTimeout(timer)
timer = null
}
}else if(!timer){
// 最后一次触发设置延时执行
timer = setTimeout(() => {
previous = +new Date()
fn.apply(this, arguments)
clearTimeout(timer)
timer = null
}, remaining)
}
}
}
// 调用
document.addEventListener('click', throttle({
fn:function(a){
console.log('防抖', this, arguments)
},
wait:1000
}))