理解函数防抖与函数节流

最近一直被面试题安利。关于节流与防抖一直出现在面试题中,刷掘金和思否也能看到,但点开看感觉很晦涩,很难坚持读下去。今天偶然间看到一个例子,关于页面窗口变化触发某个方法,首先想象一下,假如用鼠标拉缩页面,你感觉你就拉缩了一下,但是浏览器判断你是触发了很多次,如果处理的函数很复杂,这样在短时间内多次处理相同的事件,会非常的消耗内存,这时就需要用到防抖。这个例子没看懂,没关系,下面就自己理解,来聊聊到底什么是函数防抖(debounce)与函数节流(throttle)。

举个例子,用户输入身份证,然后接收用户输入的内容,发送给后台。如果在输入框上绑定一个input事件,用户输入内容时,触发事件然后获取内容,然后发送一个ajax请求。假如用户输入了一个数字,监听事件触发,发送一个请求,当用户在输入第二个数字的时候又触发事件,发送请求,无线下去直至用户停止输入。但是前面发送的信息并不是我们想要的,我们想要的是用户完整的身份证信息。但是监听事件在触发时,就会发生,也就是每输入一个数字就会触发,怎么办,这时聪明的程序猿就想到了,运用定时器来延迟函数执行。但单纯的延迟执行并不行,还是会重复执行,只不过延后了执行的时间,我们希望的是当用户停止输入后,获取内容。也就是当用户不在触发事件时定时器执行方法。

对于上面这个案例,解决办法就是在触发事件时,设置一个定时器setTimeout,但在一开始执行时会先清除本次操作中存在的定时器,然后在设置定时器,在几秒后执行该函数。当再次触发事件监听执行函数时,会清除之前的定时器,然后在执行定时器。当不在触发事件时,保留的定时器才开始生效然后执行函数。

下面来模拟一个功能,提供一个输入框,然后用户输入内容,然后处理用户输入的内容。具体怎么深度处理,我暂时没想到,这里我用打印来模拟处理的函数。下面直接看代码。

 function debounce(fn,delay){  //创建一个闭包函数,确保内部的变量不会被销毁,即保留上一次的timer
        var   timer;
        return function(){     //这里需要返回出去一个函数,让外部接受,这也是形成闭包的主要原因
            var self=this,args=arguments;  //在外部先保存一份当前this的指向,定时器里this指向的是全局,为了防止偏移,所以我们需要重新绑定this
            clearTimeout(timer);     //这里先清除上次创建的定时器,防止定时器执行 
            timer=setTimeout(function(){    //这里设置定时器,重新启动定时
                fn.apply(self,args)    //定时器内修改要执行的方法的this指向,并执行函数。
            },delay);
        }
    };
    var vel=debounce(function(e){       //外部接受闭包里面返回的函数
        console.log(e.target.value)
    },1000);
    document.querySelector("input").addEventListener("input",vel);

梳理一下为什么要这么写;注释解释了每一行的内容和为什么要这么写,但是有些概念可能还是没清楚。闭包这里不用说了,注释解释的很清楚。为什么需要保存一份this,因为后续我们需要用到定时器,因为定时器的机制就在那,定时器内this指向的是window。所以一般遇到定时器,我们都会在外部保存一份this。

为什么要先清除定时器呢,函数防抖本来就是要解决,假如高频率触发事件,会非常消耗性能,所以在触发事件时,利用定时器的功能,几秒后在执行我们需要执行的函数,这里虽然起到了延时执行,但是多次触发就会多次生成多个定时器,这样做反而没有意义。所以需要在一开始就先清除上一次保存的定时器,这样在不超过定时器规定的时间内如果再次触发了事件,那么就会消除上一次的定时器,然后在生成一个定时器,生成消除生成消除,到最后停止了,这中间其实就只生成了一个定时器。

最后就是为什么定时器内的函数要修改this指向。上面说过,定时器里的this指向的全局,假如我们不修改绑定,我们是找不到e.target.value的值的。为什么?因为在定时器里执行console这段函数时,this执行的是全局,但是我们的事件是触发在输入框这个节点上,而内容也是获取输入框内输入的内容,所以在全局上是找不到内容的。

关于函数防抖的引用场景有很多,这里就不总结了,没有什么固定的思路,想了解的可以自行百度了解一下,看看别人的东西学习学习。

理解完函数防抖,在来看看函数节流,其实函数节流和函数防抖相似,但是也有区别。函数防抖是在事件一直触发时我们去消除定时执行的任务,这样就会造成如果事件不停止,我们的任务就不会执行。而函数节流则是选择一个折中的方法,比如如果操作次数的间隔时间大于我们规定的这个时间,就执行一下方法,同时当事件结束时我们也需要再次执行一下方法,为什么?比如在短时间内高频率的输入内容,这个时间是小于规定的时间,这时就不会触发方法,所以我们需要在事件结束后,利用定时器在去执行这个方法。

还是没懂啥是节流?没关系下面,我们在来模拟一个场景。不知道大家有没有遇到过地铁高峰期。或者是春运高峰期。反正北京的春运高峰期特别在返程时,客流量特别大,就如上图那样。空间就这么大,站台的承受能力有限,如果我们不限制人流量进入站台,那么空间只会原来越拥挤,而且也存在很多隐患。怎么办,节流出现了。下面这个情景你肯定也遇到过。

首先我们不限制流量进入,但是会控制流量的进入时间。常见的就是在火车站过安检。都会设有相关人员在进站口控制进站,当一段时间内进站的人数过多时,相关人员就会拉封条,禁止旅行者进入。然后一段时间以后在打开封条,让旅行者继续进站。这就是我们说的节流。

代码如下。建议自己也敲一遍看看效果,这样更容易掌握。

function throttle(fn,threshhold) {
        var timer;
        var start=new Date;
        var threshhold=threshhold||160;
        return function () {
            var self=this,
                args=arguments,
                cur=new Date() - 0;
            clearTimeout(timer);
            if (cur-start>=threshhold){
                console.log("now",cur,cur-start);
                fn.apply(self,args);
                start=cur;
            }else {
                timer=setTimeout(function () {
                    fn.apply(self,args);
                },threshhold);
            }
        }
    }
    var mousemove=throttle(function (e) {
        console.log(e.pageX,e.pageY)
    });
    document.querySelector("#panel").addEventListener("mousemove",mousemove)

说一下代码都做了什么。首先写一个正方形,然后在正方形上绑定一个mousemove事件,然后打印出,当前鼠标的位置。你要知道你的鼠标灵敏度其实是很高的,你只要丝毫动一下就会触发这个事件的,然后就会立马打印出位置。假如我们只需要鼠标移动结束后落定的位置,用函数防抖就可以实现。如果鼠标持续晃动,我们想获取一段时间内的定位,例如每隔1秒获取鼠标落定的位置。这样就既保证了在滑动过程中也能触发我们想要执行的方法,高频率触发事件时不会出现大量的运行。

最后你还是没有看懂我说的函数节流与函数防抖,好吧,在给你安利个关于函数节流的小乐子。一位用户在登录界面输入了账号、密码。然后快速点击了10次登录按钮,结果连续弹出三次密码错误,随后提示输入密码错误超过三次,您的账号已被锁定。但是,假如用到了事件节流就不会出现这种情况了。我们只要设置在开始点击几秒以后执行一次方法,这样就不会出现连续触发连续执行方法了。

总结

函数节流、函数防抖
两者都是用来解决代码短时间内大量重复调用的方案。
当然,也是各有利弊。
在实际开发中,两者的界定也很模糊。
比如搜索关键字联想,用节流或者防抖都可以来做,拖拽DOM、监听resize等等,这两个都是可以来实现的。

两者的区别在于:
函数节流在一定时间内肯定会触发多次,但是最后不一定会触发
函数防抖可能仅在最后触发一次

如果还是不懂,你可以看看这边文章,个人觉得讲的不错,这里面的例子也是从这边文章看的

函数防抖与函数节流

一个更好理解防抖与节流的demo

陈健的个人博客,记录生活所见所感、学习笔记。专注于Web前端_SEO教程_读书心得。

2 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

返回主页看更多
狠狠的抽打博主 支付宝 扫一扫