切换主题
防抖与节流
防抖
首先我们来解决防抖,何为防抖?
等触发完事件
t
秒内不再触发事件,才执行函数;若t
秒内又触发了事件,则以该新事件为起点,重新等待t
秒再执行函数。
初版防抖
根据定义很容易写出初版代码,但这里有个坑,就是this指向
和原生的event事件对象
必须手动修复,此外我们还希望能够随时取消防抖。
JavaScript
function debounce(func, wait) {
let t
const _debounce = function () {
const ctx = this // 修复 this指向
const args = arguments // 修复 event事件对象
if (t) clearTimeout(t)
t = setTimeout(function () {
func.apply(ctx, args)
}, wait)
}
_debounce.cancel = function () {
clearTimeout(t)
t = null
}
return _debounce
}
最终版防抖
初版防抖代码有俩问题:
- 一是不能立即执行(初始化时执行一次):因此为了修复这一问题,需要增加第三个参数
immediate
,并设置默认值为true
; - 二是
func
函数可能有返回值。
JavaScript
function debounce(func, wait, immediate = true) {
let t, ret
const _debounce = function () {
const ctx = this // 修复 this指向
const args = arguments // 修复 event事件对象
if (t) clearTimeout(t)
if (immediate) { // 初始化立即执行
const now = !t // 若已经执行过,则无需再执行
t = setTimeout(function () {
t = null
}, wait)
// 返回值
if (now) ret = func.apply(ctx, args)
} else {
t = setTimeout(function () {
func.apply(ctx, args)
}, wait)
}
return ret
}
_debounce.cancel = function () {
clearTimeout(t)
t = null
}
return _debounce
}
常用的动画防抖
JavaScript
function debounce(func) {
let t
return function () {
cancelAnimationFrame(t)
t = requestAnimationFrame(func)
}
}
节流
解决了防抖,那么什么又是节流呢?
持续触发事件,每隔一段时间,只执行一次事件。
实现节流的方式有时间戳和定时器两种,但是各有优缺点:
- 时间戳:事件会立刻执行,但事件停止触发后便没有办法再执行事件;
时间戳
JavaScriptfunction throttle(func, wait) { let pre = 0 const _throttle = function () { const ctx = this const args = arguments const now = +new Date() if (wait < now - pre) { func, apply(ctx, args) pre = now } } _throttle.cancel = function () { pre = 0 } return _throttle }
- 定时器:事件不会立刻执行,而是在
t
秒后才第一次执行,但事件停止触发后依然会再执行一次事件。定时器
JavaScriptfunction throttle(func, wait) { let t const _throttle = function () { const ctx = this const args = arguments if (!t) { t = setTimeout(function () { func.apply(ctx, args) t = null }, wait) } } _throttle.cancel = function () { clearTimeout(t) t = null } return _throttle }
所以我们直接结合使用两种方式实现节流。
节流代码
JavaScript
function throttle(func, wait) {
let t, pre = 0
const _throttle = function () {
const ctx = this // 修复 this指向
const args = arguments // 修复 event事件对象
const now = +new Date()
//下次触发 func 剩余的时间
const remaining = wait - (now - pre)
if (remaining <= 0) {
func.apply(ctx, args)
pre = now
} else {
t = setTimeout(function () {
func.apply(ctx, args)
pre = now
t = null
}, remaining)
}
}
_throttle.cancel = function () {
clearTimeout(t)
t = null
pre = 0
}
return _throttle
}