# JS中防抖与节流的讲解与封装
防抖: 当一直触发某个事件时,只有当停止触发一段时间后才会 执行该做的事情
节流: 当一直触发某个事件时,不会一直执行,而是按一定时间内有规律周期性的执行一次,直至停止触发。
# 防抖
例1:
//防抖
let timer = null
window.onscroll = () => {
if (timer) {
window.clearTimeout(timer)
}
timer = window.setTimeout(() => {
console.log("执行任务")
}, 1000)
}
为了更方便的使用防抖方法,封装方法。
function debounce(func, wait = 300){
let timer = null
return function(...arg){
if(timer !== null){
clearTimeout(timer)
}
timer = setTimeout(() => {
func.apply(this, arg)
timer = null
}, wait)
}
}
//创建函数 就是 上面 return 的函数
let scrollHandler = debounce(function(e){
console.log(e)
}, 500)
//无需传参时 可直接使用 因为 scrollHandler 就是一个函数
window.onscroll = scrollHandler
//需要传参时
let scrollHandler = debounce(function(e,c){
console.log(e,c)
}, 500)
window.onscroll = function(){
scrollHandler("1", "2") //调用该函数
}
有时我们希望函数能立即执行。因此添加是否立即执行的参数
/**
* @param {function} func - 执行函数
* @param {number} wait - 等待时间
* @param {boolean} immediate - 是否立即执行
* @return {function}
*/
function debounce(func, wait = 300, immediate = false){
let timer = null
return function(...arg){
if(timer !== null){
clearTimeout(timer)
} else if(immediate){
func.apply(this, arg)
}
timer = setTimeout(() => {
func.apply(this, arg)
timer = null
}, wait)
}
}
//创建函数 就是 上面 return 得函数
let scrollHandler = debounce(function(e){
console.log(e)
}, 500)
//无需传参时 可直接使用 因为 scrollHandler 就是一个函数
window.onscroll = scrollHandler
//需要传参时
let scrollHandler = debounce(function(e,c){
console.log(e,c)
}, 500)
window.onscroll = function(){
scrollHandler("1", "2") //调用该函数
}
# 节流
例:
//节流
let timer = null
window.onscroll = () => {
if(timer === null) {
timer = window.setTimeout(() => {
console.log("执行任务")
timer = null
}, 1000)
}
}
这样就会实现 没间隔 1秒 就执行一次任务,但是不足之处有两点
- 没法一开始就执行一下任务
- 没法准确抓住最后停止时再执行一次任务
解决问题:
//节流
let timer = null, last
window.onscroll = () => {
const now = +new Date()
//如果last存在 且当前时间小于 last + 延迟时间 即非执行任务 期间
if (last && now < last + 1000) {
clearTimeout(timer)
timer = setTimeout(function() { //保证最后一次执行任务 是 最终状态
console.log("执行任务")
}, 300) //停止触发 后300毫秒立即执行
} else {
//0秒的时候不执行任务 则加上这句判断 0+wait, 0 + 2*wait ... 执行任务
if (last) {
console.log("执行任务")
}
//0秒 0+wait, 0 + 2*wait ... 执行任务
console.log("执行任务")
last = now //将当前时间给 last 用来判断下一个周期
}
}
这样即解决了上述问题,接下来就是封装起来
/**
* @param {function} func - 执行函数
* @param {number} wait - 等待时间 默认 1000毫秒
* @param {boolean} immediate - 是否立即执行 默认false
* @param {number} stopTime - 停止触发后多少毫秒后 执行任务 默认300毫秒
* @return {function}
*/
function throttle (func, wait = 1000, immediate = false, stopTime = 300){
let last, timer
return function(...arg) {
const now = +new Date()
if (last && now < last + wait) {
clearTimeout(timer)
timer = setTimeout(function() {
func.apply(this, arg)
}, stopTime)
} else {
if (!immediate) {
if (last) {
func.apply(this, arg)
}
} else {
func.apply(this, arg)
}
last = now
}
}
}
//创建函数 就是 上面 return 得函数
let scrollHandler = throttle(function(e){
console.log(e)
}, 2000)
//无需传参时 可直接使用 因为 scrollHandler 就是一个函数
window.onscroll = scrollHandler
//需要传参时
let scrollHandler = throttle(function(e,c){
console.log(e,c)
}, 2000, true)
window.onscroll = function(){
scrollHandler("1", "2") //调用该函数
}
这样防抖与节流的方法 就都好了。