关于防抖函数和节流函数的总结

一、序

关于一场糟糕的面试的自我反省,平时的工作业务逻辑不复杂,很多实现直接使用了相应的库函数和工具类框架,导致关于很多知识点都是一知半解,比如针对防抖函数和节流函数的实现原理,以及其中涉及的js基础知识,this指向等问题,是值得去深入探究的,在编写一个函数时,最先要理解该函数方法实现的流程,为了解决什么问题而设计;其次理清思路,一步步去实现方法。

二、防抖函数

  • 原理:在某个的时间段内,无论触发多少次回调,只执行最后一次;
  • 应用场景:input标签输入onchange事件的监听
  • 实现方案:使用定时器,函数第一次执行时设定一个定时器,之后调用时发现已经设定过定时器就清空之前的定时器,并重新设定一个新的定时器,如果存在没有被清空的定时器,当定时器计时结束后触发函数执行
  • 代码实现

    function debounce(fn, time) {
        let timer;
        return function() {
            let args = arguments;
            let that = this;
            if(timer) clearTimeout(timer);
            timer = setTimeout(function() {
                fn.apply(that, args)
            }, time)
        }
    }
    

三、节流函数

  • 原理:在某段时间间隔执行一次回调函数
  • 应用场景:监听滚动事件
  • 实现方案:时间差 和 延时函数两种
  • 代码实现:

    // 使用时间差判断
    function throttle(fn, time) {
      let pretime = 0;
    
      return function() {
        const args = arguments;
        let nowtime = +new Date();
        if(nowtime - pretime > time) {
          pretime = nowtime;
          fn.apply(this, args);
        }
      }
    }
    
    // 使用延时函数实现
    function throttle(fn, delay) {
      let timer;
    
      return function() {
        const args = arguments;
        const that = this;
        if(timer) return;
        timer = setTimeout(function() {
          fn.apply(that, args);
          timer = null;
        }, delay)
      }
    }
    

三、知识点

1、关于this指向问题

  • 在以上的方法中都使用了return一个匿名函数,这样在该匿名函数中this指向window
  • 在箭头函数中没有自己的this;内部this指向外层代码
  • 使用setTimeout中的回调函数,在非严格模式下this指向window,严格模式下指向undefined

    function foo() {
        console.log(this.id)
        console.log(this)
        setTimeout(function() {
            console.log(this.id)
            console.log(this)
        }, 100)
    }
    var id = 21;
    foo() //输出依次为:21,window,21,window
    foo.call({id: 42}); //输出依次为:42,{id:42},21, window
    //若将setTimeout改为箭头函数,则输出会不一样
    function foo() {
        console.log(this.id)
        console.log(this)
        setTimeout(() => {
            console.log(this.id)
            console.log(this)
        }, 100)
    }
    foo.call({id: 84}); //84, {id:84},84, {id:84}
    // 使用call方法改变了foo函数运行时其函数体内this的指向,从而使箭头函数中this的指向发生变化