如何实现节流与防抖

防抖与节流的概念

函数防抖: 触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。具体场景可以搜索框输入关键字过程中实时 请求服务器匹配搜索结果,如果不进行处理,那么就是输入框内容一直变化,导致一直发送请求。如果进行防抖处理,结果就是当我们输入内容完成后,一定时间(比如500ms)没有再输入内容,这时再触发请求。

函数节流: 高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。具体场景可以是抢购时候,由于有无数人快速点击按钮,如果每次点击都发送请求,就会给服务器造成巨大的压力,但是我们进行节流后,就会大大减少请求的次数。

防抖函数(debounce)

动手实现第一版防抖函数:

function debounce (fn, wait) {
	let timeout;
	retuen function () {
		if (timeout) {
			clearTimeout(timeout)
		}
		timeout=setTimeout(fn,wait)
	}
}

我们来写个demo验证一下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
      #search {
        width: 400px;
        margin-left: 300px;
        margin-bottom: 50px;
      }
      #showSearch {
        width: 800px;
        height: 100px;
        background: lightblue;
        color: red;
        margin-left: 300px;
      }
    </style>
  </head>
  <body>
    <input type="search" name="" id="search" />
    <div id="showSearch"></div>
  </body>
  <script>
    function debounce(fn, wait) {
      let timeout;
      return function() {
        if (timeout) {
          clearTimeout(timeout);
        }
        timeout = setTimeout(fn, wait);
      };
    }
    let search = document.querySelector("#search");
    let showSearch = document.querySelector("#showSearch");
    function getSearchInfo(e) {
       // showSearch.innerText = this.value; // 报错
      //  console.log(e); // undefined
      showSearch.innerText = search.value;
    }
    search.onkeyup = debounce(getSearchInfo, 1000);
  </script>
</html>

效果:

在这里插入图片描述

可以看到执行上面的demo,我们输入值以后不触发keyup事件就会隔1秒钟蓝色div才会出现字,如果你一直触发是不会显示的等到你停止触发后的一秒后才显示 有同学可能对第一版代码觉得挺简单的,确实简单,不过我还是要啰嗦几句,第一版的代码中,debounce函数 返回了一个匿名函数,而这匿名函数又引用了timeout这个变量,这样就形成了闭包,也就是函数的执行上下文的活动对象并不会被销毁,保存了timeout变量,才能实现我们这个 如果你在一个事件触发的n秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行 的需求。

修复this指向和event问题

显然上述中的代码还存留了二个问题就是this问题,我们this指向的不是window对象,而是指向我们的dom对象,第二个就是event对象,event对象在这里会报undefined,那么接下来我们来完善我们的第二版代码吧:

function debounce(fn, wait) {
      let timeout;
      return function() {
        let context = this;
        let args = arguments;
        if (timeout) {
          clearTimeout(timeout);
        }
        timeout = setTimeout(function() {
          fn.apply(context, args);
        }, wait);
      };
    }

第二版代码已经实现了,我们来写个demo验证下吧:

let search = document.querySelector("#search");
let showSearch = document.querySelector("#showSearch");
    function getSearchInfo(e) {
      showSearch.innerText = this.value; 
      console.log(e); 
    }
    search.onkeyup = debounce(getSearchInfo, 1000);

效果图:
在这里插入图片描述
这里涉及的知识点就是this指向和arguments、apply等,先来说说arguments属性,我们知道可以通过arguments属性获取函数的参数值,而dom事件操作中,会给 回调函数(这里回调函数是debounce函数返回的闭包函数)传递一个event参数,这样我们就可以通过arguments属性拿到event属性,那么问题二就解决啦,再来说说问题一的this指向,此时这里keyup事件的回调函数是debounce函数返回的闭包函数而不是getSearchInfo函数(但是我们希望处理业务逻辑的getSearchInfo的this指向能够指向dom对象),我们知道this的指向是我最后被谁调用,我就指向谁 ,那么我这里被search调用所以「this指向search」,但是由于有setTimeout异步操作,我们getSearchInfo函数的this指向是window(非严格模式下),所以我们需要改变getSearchInfo的指向,这样我们用apply就完美实现了.

节流函数(throttle)

立即执行:
我们可以使用时间戳,当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为 0),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。

function throttle(fn, wait) {
      let context, args;
      let previous = 0;
      return function() {
        let now = +new Date();
        context = this;
        args = arguments;
        if (now - previous > wait) {
          fn.apply(context, args);
          previous = now;
        }
      };
    }

demo使用:demo跟上述防抖用是一样

search.onkeyup = throttle(getSearchInfo, 3000);

效果图:
在这里插入图片描述
这里的节流立即执行版跟我们的防抖第一版的知识点原理一样,不再过多的赘述。

非立即执行:非立即执行就是过了n秒后我们再执行,那么我们自然而然想到「定时器」。

function throttle(fn, wait) {
      let timeout;
      return function() {
        let context = this;
        let args = arguments;
        // 这里不需要清除定时器 清除了会重新计算时间
        // 清除这个定时器不代表timeout为空
        if (timeout) {
          return false;
        }
        timeout = setTimeout(function() {
          fn.apply(context, args);
          timeout = null;
        }, wait);
      };
    }

demo使用:

function getSearchInfo(e) {
      console.log(this.value);
      // 验证的效果是 showSearch多久才能读到这个this.value
      showSearch.innerText = this.value;
    }
    search.onkeyup = throttle(getSearchInfo, 3000);

效果图:
在这里插入图片描述
这次的效果是等待过了三秒才执行,那我们来比较立即执行和非立即执行的效果

  • 立即执行会立刻执行,非立即执行会在 n 秒后第一次执行
  • 立即执行停止触发后没有办法再执行事件,非立即执行停止触发后依然会再执行一次事件

防抖和节流的区别

实际上防抖和节流都是限制一些频繁的事件触发(window 的 resize、scroll、mousedown、mousemove、keyup、keydown),但他们还是有实质性的区别的。

  • 防抖是虽然事件持续触发,但只有等事件停止触发后n秒才执行函数(如果你在时间内重新触发事件,那么时间就重新算,这是防抖的特点,可以按照这个特点选择场景)。比如生活中的坐公交,就是一定时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷,司机才开车。
  • 节流是持续触发的时候,每隔n秒执行一次函数比如人的眨眼睛,就是一定时间内眨一次。这是函数节流最形象的解释。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值