函数节流与防抖

513 阅读2分钟

事件频繁触发可能造成的问题

  1. 一些浏览器事件:window.onresize、window.mousemove等,触发的频率非常高,会造成浏览器性能问题
  2. 如果向后台发送请求,频繁触发,对浏览器造成不必要的压力

如何限制事件处理函数频繁调用

  1. 函数节流
  2. 函数防抖

函数节流(throttle)

理解

  • 在函数需要频繁触发时: 函数执行一次后,只有大于设定的执行周期后才会执行第二次
  • 适合多次事件按时间做平均分配触发

使用场景

  • 窗口调整(resize)
  • 页面滚动(scroll)
  • DOM 元素的拖拽功能实现(mousemove)
  • 抢购疯狂点击(click)
// 用来返回节流函数的工具函数
function throttle (callback, delay) {
    let pre = 0
    return function(event) {  // 节流函数/真正的事件回调函数
        const current = Date.now()
        if (current - pre > delay) {
            callback.call(this, event)  // 调用真正的事件处理函数
            pre = current
        }
    }
}

函数防抖(debounce)

理解:

  • 在函数需要频繁触发时: 在规定时间内,只让最后一次生效,前面的不生效。
  • 适合多次事件一次响应的情况

使用场景:

  • 输入框实时搜索联想(keyup/input)
  // 用来返回防抖函数的工具函数
        function debounce(callback, delay) {
            return function (event){
                // 如果上一次事件还没有真正处理,取消掉
                if (callback.hasOwnProperty('timeID')) {
                    // 清除
                    clearTimeout(callback.timeID)
                }
                // 事件发生指定时间后才调用
                // 启动定时器只是准备处理事件
                callback.timeID= setTimeout(() => {
                    // 正在处理
                    callback.call(this, event)
                    // 标记事件已经真正处理 删除准备处理的标记
                    delete callback.timeID
                },delay)
            }
        }

函数测试

  <script>
        function handleClick(event) {
            console.log('处理点击事件', this, event)
        }
        
        document.getElementById('throttle').onclick = throttle(handleClick,2000)
        document.getElementById('debounce').onclick = debounce(handleClick,2000)

    </script>

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <button id="throttle">函数节流</button>
    <button id="debounce">函数防抖</button>

    <script src="./lodash.js"></script>
    <script>
        // 用来返回节流函数的工具函数
        function throttle(callback, delay) {
            let pre = 0
            return function (event) { // 节流函数/真正的事件回调函数 this 是发生事件的标签
                const current = Date.now()
                if (current-pre>delay) {  // 只有离上一次调用callback的时间大于delay
                    callback.call(this, event)  // 调用真正处理事件的回调函数
                    pre = current
                }
            }
        }
        // 用来返回防抖函数的工具函数
        function debounce(callback, delay) {
            return function (event){
                // 如果上一次事件还没有真正处理,取消掉
                if (callback.hasOwnProperty('timeID')) {
                    // 清除
                    clearTimeout(callback.timeID)
                }
                // 事件发生指定时间后才调用
                // 启动定时器只是准备处理事件
                callback.timeID= setTimeout(() => {
                    // 正在处理
                    callback.call(this, event)
                    // 标记事件已经真正处理 删除准备处理的标记
                    delete callback.timeID
                },delay)
            }
        }
    </script>
    <script>
        function handleClick(event) {
            console.log('处理点击事件', this, event)
        }
        document.getElementById('throttle').onclick = throttle(handleClick,2000)
        document.getElementById('debounce').onclick = debounce(handleClick,2000)

    </script>
</body>
</html>