微信扫一扫看面试题

关注面试题库

我有一个牛逼的防抖,你要不要学

在开发中,我们可能碰到这样的问题:

  • 用户在搜索的时候,在不停的输入,如果每敲一个字我们就要调一次接口去查询,接口调用太频繁,会占内存给卡住。

  • 手机号、邮箱验证输入检测

  • 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

  • 页面滚动处理事件

原理分析

防抖:在事件被触发 n 秒后再执行回调函数,如果在这 n 秒内又被触发,则重新计时延迟时间

函数防抖原理:通过维护一个定时器,其延迟计时以最后一次触发为计时起点,到达延迟时间后才会触发函数执行。

防抖的实现方式分两种 “立即执行” 和 “非立即执行” ,区别在于第一次触发时,是否立即执行回调函数。

“立即执行防抖” 指事件触发后,回调函数会立即执行,之后要想触发执行回调函数,需等待 n 秒延迟

”非立即执行防抖“ 指事件触发后,回调函数不会立即执行,会在延迟时间 n 秒后执行,如果 n 秒内被调用多次,则重新计时延迟时间

理解:

  • 相当于英雄大招,施法后要等技能冷却才能再次施法(立即执行)。

  • 法师发技能的时候要读条,技能读条没完再按技能就会重新读条(非立即执行)。

手写防抖

那么理解了什么是防抖,我们可以手写一个防抖,这不仅在面试中经常出现,工作中也会遇到。

非立即执行

<body>
    <button id="debounce">点击</button>
    <script>
        window.onload=function(){
            var mydebounce = document.getElementById("debounce")
            mydebounce.addEventListener("click",debounce(sayDebounce,1000))
        }
        //防抖函数
        function debounce(fn,time) {
            let timer = null
            return function () {
                let context = this
                if(timer) clearTimeout(timer)  //清除前一个定时器
                timer = setTimeout(()=>{  //在时间间隙内再次点击不会执行fn函数
                    fn.apply(context,arguments)
                },time || 500)
            }
        }
        //要防抖的事件处理
        function sayDebounce() {
            console.log("处理防抖的事件写在这里,比如发送请求");
        }
    </script></body>

特别说明:apply 是为了改变某个函数运行时的 context 即上下文而存在的,说人话就是改变this指针的指向,函数.apply(第一个参数就是你想吧this指针指向哪,第二个参数就是你向里面传的参数(arguments));

apply()方法接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。

call()方法第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。

在js中,所有的函数再被调用的时候都会默认传入两个参数,一个是this,还有一个是arguments。在默认情况下this都是指当前的调用函数的对象。但是有时候我们需要改变this的指向,也就是说使函数可以被其他对象来调用,那么我们应该怎样做呢?这时候我们就可以使用call和apply方法了;

但是问题来了,我不想点击了按钮要等一秒它才发起请求,It is too slow!能不能点击立即执行,再次点击才需要等待技能冷却?Of course!

立即执行

function debounce(fn,time) {
            let timer = null
            return function () {
                let context = this
                let args = arguments
                if(timer) clearTimeout(timer)  //清除前一个定时器
                let callNow = !timer
                timer = setTimeout(()=>{  
                    timer = null
                },time || 500)
                if (callNow) fn.apply(context,args)
            }
        }

这个时候产品经理又提了一个新需求:能不能这个按钮立即执行,那个按钮非立即执行?

”呵......我早就猜到你会提出这种无理的需求!看剑!“,

可以实现吗?”可以,得加钱💴“。那我加个 immediate参数判断是否立即执行呗。

组合版本

<body>
        <button id="debounce">点击</button>
        <script>
            window.onload=function(){
            var mydebounce = document.getElementById("debounce")
            mydebounce.addEventListener("click",debounce(sayDebounce,1000,false))
        }
        //防抖函数
        function debounce(fn,time,immediate) {
            let timer = null
            return function () {
                let context = this
                let args = arguments
                if(timer) clearTimeout(timer)  //清除前一个定时器
                if (immediate) {  //为true立即执行
                    let callNow = !timer
                    timer = setTimeout(()=>{  
                        timer = null
                    },time || 500)
                    if (callNow) fn.apply(context,args)
                }
                else {  //非立即执行
                    timer = setTimeout(function(){
                        fn.apply(context, args)
                    }, time || 500);
                }
            }
        }
        //要防抖的事件处理
        function sayDebounce() {
            console.log("处理防抖的事件写在这里,比如发送请求");
        }
        </script>
    </body>

产品经理:”可不可以.......“, ”你走啊😠“

”能不能取消debounce函数,比如在登录时候的重新请求短信验证码,错一次要等好久“

取消debounce(终极版)

<body>
        <button id="debounce">点击</button>
        <button id="dd">取消</button>
        <script>
            var mydebounce = document.getElementById("debounce")
            var Mydebounce = document.getElementById("dd")
            var set = debounce(sayDebounce,5000,true)
            mydebounce.addEventListener("click",set)
            Mydebounce.addEventListener("click",function () { 
                set.cancel() 
            } )
        
        //防抖函数
        function debounce(fn,time,immediate) {
            let timer = null
            let debounced = function () {
                let context = this
                let args = arguments
                if(timer) clearTimeout(timer)  //清除前一个定时器
                if (immediate) {  //为true立即执行
                    let callNow = !timer
                    timer = setTimeout(()=>{  
                        timer = null
                    },time || 500)
                    if (callNow) fn.apply(context,args)
                }
                else {  //非立即执行
                    timer = setTimeout(function(){
                        fn.apply(context, args)
                    }, time || 500);
                }
            }
            debounced.cancel = function() { //取消防抖
                clearTimeout(timer);
                timer = null;
            };
            return debounced
        }
        //要防抖的事件处理
        function sayDebounce() {
            console.log("处理防抖的事件写在这里,比如发送请求");
        }
        </script>
    </body>

面试官:你能手写一个防抖看看吗?

”看我不秀死你“

更多技术

前端面试题库 

https://mp.weixin.qq.com/s/GWWBm99rjWBcsgIGXyBwig

前端技术导航大全地址:

https://webstatic-3g8wm74b420bf334-1301145096.tcloudbaseapp.com/webclass/index.html#/  

 技术分享

 1、前端技术导航大全             推荐:★★★★★

地址:前端技术导航大全

2、前端面试题库

推荐:★★★★★

地址:前端面试题库

3、开发者颜色值转换工具

推荐:★★★★★

地址 :开发者颜色值转换工具

2、前端边框阴影在线工具

推荐:★★★★★

地址:前端边框阴影在线工具 

posted @   web前端面试小助手  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示