详解防抖的概念并且手动实现防抖函数

一、防抖的概念和使用库实现防抖

1.1. 认识防抖
  • 防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时

  • 防抖:在事件触发时,相应的回调函数并不会立即执行,而是会等待n秒。当事件频繁触发时,函数的触发也会被频繁推迟。只有等待了n秒后,事件都没有被触发,对应的回调函数才会被执行

    const inputEl = document.querySelector("input")
    let count = 0
    
    // 为了实现能像淘宝那样,用户输入一个词,就有相应的联想词供用户选择 
    // 我监听了这个输入框的输入,当用户在输入框每输入一次新的词,监听input输入的这个回调函数就会发送一次网络请求
    // 但是这样做的弊端就是,如果用户明确的知道自己想要什么东西,并且快速输入了商品名称时,这个回调函数依然会被多次调用。但其实这种情况就不应该再针对每个词,都让监听input的回调函数发送网络请求去做联想了,因为这样会使服务器的压力很大
    // 我们应该实现的功能/正确做法应该是:当用户输入一个词停顿了n秒之后,就请求数据做联想,但是如果用户快速输入了一个商品名称时,那么前面的“那几个词”就不应该对它们做联想
    // 这时我们就可以使用“防抖”:意思就是当监听到有一个词被输入时,对监听的这个操作的回调函数延迟n秒执行,但是如果在n秒内又监听到新的词被输入了,那么就取消对上次回调函数的执行,转而继续对监听到的输入的新的词调起的回调函数做延迟n秒执行的操作,如此循环。
    inputEl.oninput = function() {
        console.log(`发送网络请求${count++}`, this.value)
    }
    
1.2. underscore实现防抖
  • underscore是JavaScript的库,里面实现了很多函数,其中就有实现防抖功能的函数

  • 利用underscore实现防抖的具体步骤

    • 通过CDN引入网络的文件

    • 调用其中的debounce函数

      _.debounce(fn, 400)
      
    <input type="text">
    
    <!-- 通过CDN引入网络的文件 -->
    <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.6/underscore-umd-min.js"></script>
    <script>
    
        const inputEl = document.querySelector("input")
        let count = 0
    
        // 这就实现防抖了
        inputEl.oninput = _.debounce(function() {
            console.log(`发送网络请求${count++}`, this.value)
        }, 400)
    
    </script>
    

二、手写防抖实现

2.1. 基本实现
<body>

<input type="text">

    <script>

        function zdDebounce(fn, delay) {

          // 3.定义一个变量用来接收setTimeout的Id
          let timer = null

          // 1.这个函数将会被绑定在事件上,如果事件被频繁触发,那么绑定在事件上的_debounce函数也会被频繁触发
          const _debounce = function() {

            // 4.如果第二次事件被触发的时候,第一次事件触发的回调函数没执行,那就使第一次事件触发的回调函数取消执行,转而对第二次事件触发的回调函数重新进行计时
            if (timer) clearTimeout(timer)

            // 2.利用setTimeout对事件触发的回调函数的执行延迟
            timer = setTimeout(() => {
              fn()
              timer = null
            }, delay)

          }

          return _debounce
        }

    </script>

    <script>

            const inputEl = document.querySelector("input")
            let count = 0

            inputEl.oninput = zdDebounce(function() {
                console.log(`发送网络请求${count++}`, this.value)
            }, 2000)

    </script>


</body>
2.2. this和参数的绑定
<body>

    <input type="text">

    <script>

        // 2.this和参数的绑定
        function zdDebounce(fn, delay) {

            let timer = null
            function _debounce(...args) {
                if (timer) clearTimeout(timer)

                timer = setTimeout(() => {
                    fn.apply(this, args)
                    timer = null
                }, delay)

            }

            return _debounce
        }

    </script>

    <script>

        const inputEl = document.querySelector("input")
        let count = 0

        inputEl.oninput = zdDebounce(function(event) {
            console.log(`发送网络请求${count++}`, this.value, event)
        }, 2000)

    </script>


</body>
2.3. 立即执行:事件第一次被触发就执行一次回调函数
<body>

    <input type="text">

    <script>

        // 3.立即执行:事件第一次触发就执行一次相应的回调函数
        function zdDebounce(fn, delay, immediate = false) {

            let timer = null
            let isFlag = true

            function _debounce(...args) {
                if (timer) clearTimeout(timer)

                if (immediate && isFlag) {
                    fn.apply(this, args)
                    isFlag = false
                    return
                }

                timer = setTimeout(() => {
                    fn.apply(this, args)
                    timer = null
                    isFlag = true
                }, delay)

            }

            return _debounce
        }

    </script>

    <script>

        const inputEl = document.querySelector("input")
        let count = 0

        inputEl.oninput = zdDebounce(function(event) {
            console.log(`发送网络请求${count++}`, this.value, event)
        }, 2000, true)

    </script>


</body>
2.4. 取消最后一次执行的功能
<body>

    <input type="text">
    <button class="cancel">取消</button>

    <script>

        // 4.添加取消执行的功能:要取消执行,就是拿到timer,点击了取消按钮时,清除timer就好了
        function zdDebounce(fn, delay, immediate = false) {

            let timer = null
            let isFlag = true

            function _debounce(...args) {
                if (timer) clearTimeout(timer)

                if (immediate && isFlag) {
                    fn.apply(this, args)
                    isFlag = false
                    return
                }

                timer = setTimeout(() => {
                    fn.apply(this, args)
                    timer = null
                    isFlag = true
                }, delay)

            }

            // 函数也是一个对象,所以可以给它添加一个cancel方法
            _debounce.cancel = function() {
                clearTimeout(timer)
            }

            return _debounce
        }

    </script>

    <script>

        const inputEl = document.querySelector("input")
        const cancelEl = document.querySelector(".cancel")
        let count = 0

        // 这个返回的函数(对象)中是有cancel这个方法的
        const returnValue = zdDebounce(function(event) {
            console.log(`发送网络请求${count++}`, this.value, event)
        }, 2000, true)

        inputEl.oninput = returnValue

        cancelEl.onclick = function() {
            returnValue.cancel()
        }

    </script>

</body>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值