防抖(Debounce)

345 阅读1分钟

防抖(Debounce)是一种常用于限制连续触发事件的执行频率的技术。防抖的核心思想是在一定时间内只执行一次,如果在指定时间内再次触发,就重新计时。这在处理一些频繁触发的事件(如输入框输入、窗口大小变化等)时非常有用。

以下是一个简单的 JavaScript 防抖函数的实现,这个防抖函数接受三个参数:

  • func:要执行的函数
  • wait:等待的时间间隔
  • immediate:是否立即执行
function debounce(func, wait = 100, options = {}) {
    if (typeof func !== 'function') {
        throw new TypeError(`Expected the first parameter to be a function, got \`${typeof func}\`.`)
    }

    if (wait < 0) {
        throw new RangeError('`wait` must not be negative.')
    }

    const { immediate } = typeof options === 'boolean' ? { immediate: options } : options

    let storedContext
    let storedArguments
    let timeoutId
    let result
    let timestamp

    function later() {
        const last = Date.now() - timestamp

        if (last < wait && last >= 0) {
            timeoutId = setTimeout(later, wait - last)
        } else {
            timeoutId = undefined

            if (!immediate) {
                result = func.apply(storedContext, storedArguments)
                storedContext = undefined
                storedArguments = undefined
            }
        }
    }

    const debounced = function (...args) {
        if (storedContext && this !== storedContext) {
            throw new Error('Debounced method called with different contexts.')
        }

        storedContext = this
        storedArguments = args
        timestamp = Date.now()

        const callNow = immediate && !timeoutId

        if (!timeoutId) {
            timeoutId = setTimeout(later, wait)
        }

        if (callNow) {
            result = func.apply(storedContext, storedArguments)
            storedContext = undefined
            storedArguments = undefined
        }

        return result
    }

    debounced.clear = () => {
        if (timeoutId) {
            clearTimeout(timeoutId)
            timeoutId = undefined
        }
    }

    debounced.flush = () => {
        if (timeoutId) {
            result = func.apply(storedContext, storedArguments)
            storedContext = undefined
            storedArguments = undefined

            clearTimeout(timeoutId)
            timeoutId = undefined
        }
    }

    return debounced
}