防抖与节流的个人理解及其对应的应用场景

什么是防抖和节流,他们的应用场景有哪些

防抖 (debounce)

防抖,顾名思义,防止抖动,以免把一次事件误认为多次,敲键盘就是一个每天都会接触到的防抖操作。

想要了解一个概念,必先了解概念所应用的场景。在 JS 这个世界中,有哪些防抖的场景呢

  • 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
  • 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
  • 文本编辑器实时保存,当无任何更改操作一秒后进行保存

代码如下,可以看出来「防抖重在清零 clearTimeout(timer)

function debounce (f, wait) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      f(...args)
    }, wait)
  }
}

节流 (throttle)

节流,顾名思义,控制水的流量。控制事件发生的频率,如控制为1s发生一次,甚至1分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 类似。

  • scroll 事件,每隔一秒计算一次位置信息等
  • 浏览器播放事件,每个一秒计算一次进度信息等
  • input 框实时搜索并发送请求展示下拉列表,没隔一秒发送一次请求 (也可做防抖)

代码如下,可以看出来「节流重在开关锁 timer=null

function throttle (f, wait) {
  let timer
  return (...args) => {
    if (timer) { return }
    timer = setTimeout(() => {
      f(...args)
      timer = null
    }, wait)
  }
}

总结 (简要答案)

防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零 clearTimeout

节流:控制流量,单位时间内事件只能触发一次,如果服务器端的限流即 Rate Limit。代码实现重在开锁关锁 timer=timeout; timer=null
在这里插入图片描述

防抖与节流

面试准备 - JS 防抖与节流

函数防抖和节流,都是控制事件触发频率的方法。 

https://zhuanlan.zhihu.com/p/72923073

节流: 防止暴力点击(单位时间内第一次执行)放暴力点击:最好的办法,通过设置布尔类型的变量(比如按钮;disabled  或者loading)来控制,判断状态,如果为true,则 return 掉 

防抖: 搜索框的运用(单位时间内最后一次执行)

 

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

看一个?(栗子):

<body>
    没有防抖的input:<input type="text" id="unDebounce"><br>
    防抖后的:      <input type="text"><br>
    节流后的:       <input type="text">
    <script>
        //没有防抖的
            function ajax(content){
                console.log('ajax request '+content)
            }
            let inputa=document.getElementById("unDebounce")
            inputa.addEventListener("keyup",function(e){
                ajax(e.target.value)
            })
        //-----------------------------------------
        </script>
</body>

可以看到,我们只要按下键盘,就会触发这次ajax请求。

不仅从资源上来说是很浪费的行为,而且实际应用中,用户也是输出完整的字符后,才会请求。下面我们优化一下:

 没有防抖的input:<input type="text" id="unDebounce"><br>
    防抖后的:      <input type="text" id="Debounce"><br>
    节流后的:       <input type="text">
    <script>
        //防抖的
            function ajax(content){
                console.log('ajax request '+content)
            }

            function  debounce(fun,time){
                return function(args){
                let that=this;
                clearTimeout(fun.id);
                fun.id=setTimeout(function(){
                    fun.call(that,args)
                },time)
                }
            }
           let inputDebounce=document.getElementById("Debounce")
           let debounceAjax = debounce(ajax, 500)

            inputDebounce.addEventListener('keyup',function(e){
                debounceAjax(e.target.value)
            })
        </script>

 

 

关于页面窗口变化触发某个方法,首先想象一下,假如用鼠标拉缩页面,你感觉你就拉缩了一下,但是浏览器判断你是触发了很多次,如果处理的函数很复杂,这样在短时间内多次处理相同的事件,会非常的消耗内存,这时就需要用到防抖。这个例子没看懂,没关系,下面就自己理解,来聊聊到底什么是函数防抖(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执行的是全局,但是我们的事件是触发在输入框这个节点上,而内容也是获取输入框内输入的内容,所以在全局上是找不到内容的。

  • 用户在搜索的时候,在不停敲字,如果每敲一个字我们就要调一次接口,接口调用太频繁,给卡住了。
  • 输入(电话号码等输如)用防抖来节约请求资源。
  • 搜索(每输入一次就执行过滤一次)
  • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次

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

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

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

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

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等等,这两个都是可以来实现的。

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

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值