function/debounce.jsjavascript
/**
* 创建防抖函数,支持前缘、尾调用以及 cancel/flush 控制。
* @function debounce
* @memberof module:function
* @param {Function} fn 目标函数
* @param {number} [wait=0] 等待毫秒数
* @param {Object} [options] 执行选项
* @param {boolean} [options.leading=false] 是否在等待开始时立即执行
* @param {boolean} [options.trailing=true] 是否在等待结束后执行
* @returns {Function} 带 cancel 和 flush 方法的防抖函数
* @throws {TypeError|RangeError} 参数类型或 wait 不合法时抛出
*/
function debounce(fn, wait = 0, options = {}) {
if (typeof fn !== 'function') throw new TypeError('fn must be a function')
if (!Number.isFinite(wait) || wait < 0) throw new RangeError('wait must be a non-negative finite number')
if (options === null || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('options must be an object')
const leading = options.leading ?? false
const trailing = options.trailing ?? true
if (typeof leading !== 'boolean' || typeof trailing !== 'boolean') throw new TypeError('leading and trailing must be booleans')
let timerId = null
let lastArgs
let lastThis
let result
const invoke = () => {
const args = lastArgs
const context = lastThis
lastArgs = undefined
lastThis = undefined
result = fn.apply(context, args)
return result
}
const debounced = function (...args) {
const callNow = leading && timerId === null
lastArgs = args
lastThis = this
if (timerId !== null) clearTimeout(timerId)
timerId = setTimeout(() => {
timerId = null
if (trailing && lastArgs) invoke()
else {
lastArgs = undefined
lastThis = undefined
}
}, wait)
if (callNow) invoke()
return result
}
debounced.cancel = () => {
if (timerId !== null) clearTimeout(timerId)
timerId = null
lastArgs = undefined
lastThis = undefined
}
debounced.flush = () => {
if (timerId === null) return result
clearTimeout(timerId)
timerId = null
if (trailing && lastArgs) return invoke()
lastArgs = undefined
lastThis = undefined
return result
}
return debounced
}
export default debounce