• JS面试题----防抖函数


    第1题:防抖函数

    • 原理:当持续触发一个事件时,在n秒内,事件没有再次触发,此时才会执行回调;如果n秒内,又触发了事件,就重新计时

    • 适用场景:

      • search远程搜索框:防止用户不断输入过程中,不断请求资源,n秒内只发送1次,用防抖来节约资源
      • 按钮提交场景,比如点赞,表单提交等,防止多次提交
      • 监听浏览器的缩放事件
      • 监听浏览器的滚动事件
    • 辅助理解:在你坐电梯时,当一直有人进电梯(连续触发),电梯门不会关闭,在一定时间间隔内没有人进入(停止连续触发)才会关闭。

    2022-06-22 22.38.56

    <body>
        <!-- 输入框 -->
        不防抖输入框<input>
        <script>
            //获取输入框
            const inputEl = document.querySelector('input')
    
            //触发的函数
            let count = 0
            const inputChange = function(){
                console.log(`${++count}次调用inputChange方法`)
            }
    
            //输入触发
            inputEl.oninput = inputChange
        </script>
    </body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    自定防抖函数

    防抖函数实现的原理是,传入要执行的函数,以及需要等待推迟的时间,在经过一系列的处理之后,再去执行传入的函数。

    第一步:基础防抖

    定义setTimeout推迟函数的执行时间,clearTimeout用于在输入下一个字符还没有超过1秒钟时清除计时器,创建新的计时器,如果没有清除的话,那么相当于每个字符都会隔1s调用方法。

    2022-06-22 22.55.26

    <body>
    
        <!-- 
            定义setTimeout推迟函数的执行时间,clearTimeout用于在输入下一个字符还没有超过1秒钟时清除计时器,创建新的计时器,
            如果没有清除的话,那么相当于每个字符都会隔1s调用方法。
         -->
        基础版防抖<input>
        <script>
            const inputEl = document.querySelector('input')
    
            let count = 0
            const inputChange = function(){
                console.log(`${++count}次调用inputChange方法`)
            }
    
            //基础防抖函数
            const debounce = function (fn, delay) {
                //定时器变量
                let timer = null;
                const _debounce = function () {
                    //定时器不为null清除定时器
                    if (timer) clearTimeout(timer);
                    //定时器为空重新创建定时器
                    timer = setTimeout(()=>{
                        //执行回调函数
                        fn()
                    }, delay);
                };
                return _debounce;
            };
            inputEl.oninput = debounce(inputChange, 1000);
    
        </script>
    </body>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    第二不:拓展this 和参数

    在上一步定义函数时,this对象和event参数都被丢失了,在这里要把他们找回来,只需要在执行fn函数时,通过call/apply/bind来改变this的执行,以及传入参数即可。

    2022-06-22 23.39.49

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>扩展this和参数</title>
    </head>
    <body>
        扩展this和参数<input>
        <script>
            const inputEl = document.querySelector('input')
    
            let count = 0
            //添加
            const inputChange = function(e){
                console.log(`${++count}次调用,输入内容为${e.target.value}`)
            }
    
            const debounce = function (fn, delay) {
                let timer = null;
                const _debounce = function (...args) {
                    if (timer) clearTimeout(timer);
                    timer = setTimeout(()=>{
                        //通过call/apply/bind 指定this,以及传入的事件对象
                        fn.apply(this,args)
                    }, delay);
                };
                return _debounce;
            };
            inputEl.oninput = debounce(inputChange, 1000);
    
    
        </script>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    第三步:函数立即执行

    在我们上面定义的防抖函数中,是没有立即执行的,也就是在输入第一个字符"j"的时候,是不会调用函数,可能存在一些场景,等到用户输入完成再调用显得响应比较缓慢,需要在第一个字符输入时就进行一次调用。
    这里可以对于传入的函数增加一个参数,表示是否需要立即执行,默认不需要,为false,函数里在使用一个变量来保存是否需要首次执行,当首次执行完成后,再将这个变量置为false

    2022-06-22 23.49.08

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>函数立即执行</title>
    </head>
    <body>
        函数立即执行<input>
        <script>
            const inputEl = document.querySelector('input')
    
            let count = 0
            const inputChange = function(e){
                console.log(`${++count}次调用,输入内容为${e.target.value}`)
            }
    
    
            
            
            const debounce = function (fn, delay, isImmediate = false) {
                let timer = null;
                //立即执行配置变量,默认为false:不立即执行
                let isExcute = isImmediate;
                const _debounce = function (...args) {
                    //判断立即执行
                    if (isExcute) {
                        //执行完一次后立即关闭
                        fn.apply(this, args);
                        isExcute = false;
                    }
                    if (timer) clearTimeout(timer);
                    timer = setTimeout(()=>{
                        //通过call/apply/bind 指定this,以及传入的事件对象
                        fn.apply(this,args)
                      	isExcute = isImmediate;
                    }, delay);
                };
                return _debounce;
            };
            //配置立即执行
            inputEl.oninput = debounce(inputChange, 1000, true);
    
        </script>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    第四步:取消

    仔细一看,我们的防抖函数不能够取消呀,只要到了时间就一定会执行,万一当用户输完内容之后,还没有到1秒钟,但是他已经关掉窗口了呢,考虑到这种情况,我们得把取消功能安排上!
    函数也是一个对象,我们可以给函数对象上再绑定一个函数,在return的_debounce上绑定一个cancel方法,当需要取消的时候执行cancel方法

    2022-06-23 00.04.43

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>函数立即执行</title>
    </head>
    <body>
        函数立即执行<input> <br>
        <button>取消</button>
       
        <script>
            const inputEl = document.querySelector('input')
            const cancelBtn = document.querySelector("button");
    
    
            let count = 0
            const inputChange = function(e){
                console.log(`${++count}次调用,输入内容为${e.target.value}`)
            }
    
    
            const debounce = function (fn, delay, isImmediate = false) {
                let timer = null;
                let isExcute = isImmediate;
                const _debounce = function (...args) {
                    if (isExcute) {
                        fn.apply(this, args);
                        isExcute = false;
                    }
                    if (timer) clearTimeout(timer);
                    timer = setTimeout(()=>{
                        fn.apply(this,args)
                      	isExcute = isImmediate;
                    }, delay);
                };
                //这里使用到了闭包的性质
                _debounce.cancel = function () {
                    if (timer) clearTimeout(timer);
                };
                return _debounce;
            };
            debounceFn = debounce(inputChange, 1000, true);
            //取消调用
            inputEl.oninput = debounceFn;
            cancelBtn.onclick = debounceFn.cancel;
        </script>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    第五步:函数返回值

    上面定义的"防抖"函数是没有返回值的,如果说在执行完成之后,希望得到执行的结果,那么也有两种方式可以获取到
    回调函数
    在防抖函数的入参中增加第四个参数,是一个函数,用来获取防抖函数执行完成的结果

    2022-06-23 00.19.05

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>函数返回值</title>
    </head>
    
    <body>
        函数返回值<input>
        <script>
            const inputEl = document.querySelector('input')
    
            let count = 0
            const inputChange = function (e) {
                console.log(`${++count}次调用,输入内容为${e.target.value}`);
                return '执行结果';
            }
    
    
            const debounce = function (fn, delay, isImmediate = false, callbackFn) {
    
                let timer = null;
                let isExcute = isImmediate;
                const _debounce = function (...args) {
                    if (isExcute) {
                        const result = fn.apply(this, args);
                        callbackFn(result)
                        isExcute = false;
                    }
                    if (timer) clearTimeout(timer);
                    timer = setTimeout(() => {
                        //调用回调传入结果
                        const result = fn.apply(this, args)
                        callbackFn(result)
                        isExcute = isImmediate;
                    }, delay);
                };
                return _debounce;
            };
            //回调接收result
            const debounceFn = debounce(inputChange, 1000, true, function (result) {
                console.log('获取执行的结果', result)
            });
            inputEl.oninput = debounceFn;
    
        </script>
    </body>
    
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    promise

    防抖函数的返回函数中,通过promise来返回成功或失败的结果,以下代码只判断了成功的执行条件,还可以加上失败的处理。
    通过promise包裹的异步函数要经过调用才能获取响应结果,所以将防抖函数放在新函数中,将新函数作为oninput事件响应的函数。

    2022-06-23 00.28.32

    在开发中使用防抖函数优化项目的性能,可以按如上方式自定义,也可以使用第三方库。

    lodash

    https://www.lodashjs.com/docs/lodash.debounce#_debouncefunc-wait0-options

  • 相关阅读:
    Linux篇【5】:Linux 进程概念(上)
    Kali Linux 2024.1
    Excel打开后关闭就马上跳出 Visual c++ Runtime Error R6025
    【探索AI】四:AI(人工智能)自然语言处理(NLP)
    虚拟IP技术
    allure测试报告生成逻辑--解决在Jenkins里打开allure报告页面后空白显示无数据问题(以window环境为例)
    一个来自内蒙 正式工作两年的攻城狮的独白以及总结
    GOM登录器配置免费版生成图文教程
    上海亚商投顾:三大指数小幅下跌 CPO、算力板块集体爆发
    大数据Doris(十一):添加FS_BROKER步骤
  • 原文地址:https://blog.csdn.net/weixin_44235759/article/details/125419211