工作中,有时需要绑定一些持续触发的事件,如 resize、scroll、input、mousemove 等,但我们并不希望在事件持续触发的过程中频繁地去执行函数。
因为这不利于性能优化,如果是调用接口,还会对服务器造成极大的压力。所以我们需要使用“防抖”和“节流”来控制函数的调用次数,提高性能。
一、函数防抖
定义:事件响应函数在一段时间后才执行,如果在这段时间内再次调用,则重新计算执行时间;当预定的时间内没有再次调用该函数,则执行事件响应函数。
1. 最简单版本
function debounce(fn, wait) {
let timer = null
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn()
timer = null
}, wait)
}
}
demo:js.jirengu.com/hohujoyese/…
2. 指定 this
function debounce(fn, wait) {
let timer = null
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this)
timer = null
}, wait)
}
}
demo: js.jirengu.com/popoborabi/…
3. 传参
function debounce(fn, wait) {
let timer = null
return function (...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this, ...args)
timer = null
}, wait)
}
}
demo: js.jirengu.com/yaqivoxuca/…
4. 立即执行(第一次执行)
function debounce(fn, wait, immediate) {
let timer = null
return function (...args) {
if (timer) clearTimeout(timer)
if (immediate) {
let callNow = !timer
if (callNow) fn.call(this, ...args)
timer = setTimeout(() => {
timer = null
}, wait)
} else {
timer = setTimeout(() => {
fn.call(this, ...args)
timer = null
}, wait)
}
}
}
demo: js.jirengu.com/wiwuwacoli/…
5. 返回值
function debounce(fn, wait, immediate) {
let timer = null
let result = null
return function (...args) {
if (timer) clearTimeout(timer)
if (immediate) {
let callNow = !timer
if (callNow) result = fn.call(this, ...args)
timer = setTimeout(() => {
timer = null
}, wait)
} else {
timer = setTimeout(() => {
result = fn.call(this, ...args)
timer = null
}, wait)
}
return result
}
}
demo: js.jirengu.com/laqeyinipe/…
6. 取消事件
function debounce(fn, wait, immediate) {
let timer = null
let result = null
let _debounce = function (...args) {
if (timer) clearTimeout(timer)
if (immediate) {
let callNow = !timer
if (callNow) result = fn.call(this, ...args)
timer = setTimeout(() => {
timer = null
}, wait)
} else {
timer = setTimeout(() => {
result = fn.call(this, ...args)
timer = null
}, wait)
}
return result
}
_debounce.cancel = function () {
clearTimeout(timer)
timer = null
}
return _debounce
}
demo: js.jirengu.com/fusuruduhu/…
二、函数节流
定义:如果持续触发事件,每隔一段时间,只执行一次事件。
1. 时间戳版(第一次立即执行,最后一次不执行)
function throttle(fn, wait) {
let old = 0
return function (...args) {
let now = new Date().getTime()
if (now - old > wait) {
fn.call(this, ...args)
old = now
}
}
}
demo: js.jirengu.com/siqesinari/…
2. 定时器版(第一次不立即执行,最后一次会执行)
function throttle(fn, wait) {
let timer = null
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.call(this, ...args)
timer = null
}, wait)
}
}
}
demo: js.jirengu.com/fakawuxigu/…
3. 时间戳+计时器版(第一次立即执行,最后一次也会执行)
function throttle(fn, wait) {
let old = 0
let timer = null
return function (...args) {
let now = new Date().getTime()
if (now - old > wait) {
if (timer) {
clearTimeout(timer)
timer = null
}
fn.call(this, ...args)
old = now
}
if (!timer) {
timer = setTimeout(() => {
fn.call(this, ...args)
old = new Date().getTime()
timer = null
}, wait)
}
}
}