js的debounce防抖原理

747 阅读2分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

是什么?

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
防抖是优化高频率执行js代码的一种手段
你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行。

为什么用?

在日常开发中,我们常常需要考虑很多用户有可能的操作,也需要经常考虑项目的性能问题。比如:有一个按钮,每次点击按钮之后,都会发起一个请求,这时,如果用户对这个按钮疯狂点击,就会极大地浪费资源,降低前端性能;又或者,我们有一个输入框,每输入一个字,都会发送请求给后端,后端返回匹配的人名,这个时候我们考虑性能的问题话,不必立马给后端发送请求,可以说让用户输入停止300ms后再次发送请求。这个时候,我们就可以考虑使用防抖了

怎么用?

在项目中,我们可能用得比较多lodash这个插件,他这个插件的防抖函数,也比较全面,可以立即执行,也可以延迟执行。
在vue项目中,我们会这么直接使用

import debounce from 'lodash/debounce';
methods: {
    fn: debounce(function () {
        // 执行操作
    }, 200)
}

他还有第三个参数,是一个对象,里面有两个互斥的属性
leading&trailing
leading: 默认为false 为true时,代表延迟前执行 trailing: 默认为true,为true时,代表延迟后执行

实现

然后自己实现一个简单的吧

function debounce(fn,wait,options) {
    let leading = false
    if (isObject(options)) {
        leading = !!options.leading
    }
    let timer, hasOperate
    // 这边我返回的是箭头函数,所以不用新建一个this变量去接收
    return (...args) => {
        // clearTimeout之后,timer还是有值的
        if(timer) clearTimeout(timer)
        // 如果是立即执行的
        if(leading) {
            hasOperate = !timer
            timer = setTimeout(() => {
                timer = null
            },wait)
            // 如果执行过一次之后,hasOperate就变成了true,永远不会再执行了
            if(hasOperate) fn.apply(this, args)
        }else {
            timer = setTimeout(() => {
                // 用...args,那args就是一个数组
                fn.apply(this, args)
            },wait)
        }
    }
}
使用
<div>
    <div id="button1">点击</div>
</div>
<script>
    function isObject(value) {
        const type = typeof value
        return value != null && (type === 'object' || type === 'function')
    }
    const button1 = document.getElementById('button1')
    function cl(e) {
        console.log('e ', e);
        return e
    }
    button1.addEventListener('click', debounce(cl,1000,{leading:true}))
</script>