关于防抖,节流函数

  1. 防抖函数

 function debounce(func, wait) {
  let timeout;
  return function () {
    const context = this;
    const args = [...arguments];
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args)//改变this的指向,或者写成箭头函数传入this,此时指针指向上下文
    }, wait);
  }
}

为什么此时要改变this指针的问题,因为一般我们使用防抖函数都是用在监听输入或者图片加载,比如在蘑菇街项目里面,我们需要对this.$refs.scroll.reresh(),此时的this我们要指向到Vue实例(虽然本身就是指向vue实例,但是如果在其他地方这个this万一不是指向Vue完蛋)

官方点的解释

防抖函数的代码使用这两行代码来获取 this 和 参数,是为了让 debounce 函数最终返回的函数 this 指向不变以及依旧能接受到 e 参数。

举例:

<!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>debounce</title>
</head>

<body>
  <div>
    <input id="search" type="text" />
    <div id="result"></div>
    <script>
      let count = 0;
      let result = document.getElementById("result");
      let search = document.getElementById("search");
      search.addEventListener("keyup",debounce(onSearch, 1500));
      
      function onSearch() {
        console.log(this);
        result.innerHTML = ++count;
      }

      function debounce(func, wait = 1000) {
        let timeout;
        console.log(this);
        return function () {
          clearTimeout(timeout);
          timeout = setTimeout(() => {
            func.call(this);
          }, wait);
        }
      }
    </script>
  </div>
</body>

</html>

此时发现我们还没有keyup已经在打印debounce函数里的this了,此时指向window,但是当我们按下键盘的时候,不再执行debounce函数,而是执行了闭包里的函数?并且我们如果不是一开始回调debounc函数,而是在function里或者是箭头函数里,此时的this指向了window??

上述问题:

为什么执行闭包里的函数,这是因为本来写法就是一个函数嵌套回调的内容,而此时我们把debounce作为执行函数,然后一开始调用了 所以就执行了一次,然后这个debounce函数return了一个闭包,当然后面只执行闭包里的内容啦v!!自己真的有点蠢1

this指向是因为属于dom元素的回调,然后相当于是dom元素在调用这个函数,所以this指向了dom。相当于这是dom元素对象的一个内置方法属性。

举个例子:

let obj = {
        test: function(){
          console.log(this);//打印为obj对象
        }
      }
      console.log(obj.test());

关于防抖,节流函数_第1张图片

  1. 节流函数:

节流函数指的是在一定时间内,只执行一次这个函数

  • 时间戳版本:
//时间戳版本
function throttle1(fn, wait) {
        var pre = Date.now();
        return function () {
          var context = this;
          var args = arguments;
          var now = Date.now();
          if (now - pre >= wait) {
            fn.apply(context, args);
            pre = Date.now();
          }
        }
      }
  • 定时器版本:
// 定时器方案
function throttle(fn,wait){
    var timer = null;
    return function(){
        var context = this;
        var args = arguments;
        if(!timer){
            timer = setTimeout(function(){
                fn.apply(context,args);
                timer = null;//注意不是clearTimeout根据测试,timer是分配一个随机数字id,clearTimeout后,timer的变量指向数字id还在, 只是定时器停止了。
把timer赋值为null,是为了释放内存,同时也方便布尔判断吧
            },wait)
        }
    }
}

注意和防抖函数不同的是把timer = null

根据测试,timer是分配一个随机数字id,clearTimeout后,timer的变量指向数字id还在, 只是定时器停止了。
把timer赋值为null,是为了释放内存,同时也方便布尔判断吧

很多人可能会误认为定时器复制为null就等于清楚了定时器,这个是个错误的理解。上面说了 timer只不过是setInterval的一个引用而已,你赋值timer为null,只不过是清除了timer这个变量,清除了timer的内存占用,setInterval还是存在的

说下这个定时器版本的步骤

当我们触发第一次时,进入这个定时器,如果我们点击很快还没到这个wait时间第二次触发的时候,此时timer还没有赋值为null,所以不会进入此定时器,这也就导致在wait时间内只会执行一次,无论你怎么触发这个函数

你可能感兴趣的:(脚踏实地)