头图

前言

本篇章主要讲述防抖、节流的原理和应用场景,并且手动实现防抖、节流函数。

面试回答

1.防抖节流:防抖是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段事件执行。防抖触发高频率事件时n秒后只会执行一次,如果n秒内再次触发,则会重新计算。节流触发高频事件,相当于控制流量,它是在指定的单位时间内触发一次。可以通过时间戳来设置间隔,如果小于间隔就不执行,大于间隔就执行。也可以通过定时器来实现,如果存在定时器就不执行,如果不存在就执行并清空定时器。

知识点

节流和防抖的区别在于,如果当前有等待执行的延时函数时,防抖是创建一个新的延时函数去替换旧的,节流是直接return,等待旧的函数执行完毕。

1.防抖

触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。

思路:每次触发事件时都取消之前的延时调用方法

实例场景:input输入框,输入后查询接口获得下拉框,希望输完后只发一次请求

基础实例:

function debounce(fn) {
      let timeout = null; // 创建一个标记用来存放定时器的返回值
      console.log('1-------------',timeout)
      return function () {
          console.log('2-------------',timeout)
        if(timeout){
             clearTimeout(timeout); 
             // 每当用户输入的时候把前一个 setTimeout clear 掉
        }
        timeout = setTimeout(() => { 
    // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
          fn.apply(this, arguments);
        }, 2000);
      };
}
function sayHi() {
    console.log('防抖成功');
}
window.addEventListener('resize', debounce(sayHi)); 

//这边应用了函数柯里化,即利用函数执行可以形成一个不销毁的私有作用域(timeout),把预先处理的内容都存在这个不销毁的作用域里面(除了第一次外不再执行let timeout = null ),并且返回一个小函数,以后要执行的就是这个小函数。
//首先,return的函数会直接执行
//其次,当触发resize时,会直接执行return的函数,而没有再执行let timeout = null

优化实例:

data(){
  return {
    inputContent:'',  //input内容
    inputSearch:'',  //input下拉框内容
    timeout:null  //用来存放定时器的返回值
  }
},
methods:{
  debounce(){
    if(this.timeout){
      clearTimeout(this.timeout)//每当用户输入的时候把前一个setTimeout 清除掉
    }
//然后又新建一个setTimeout,这样就能保住输入字符后的setTimeout间隔内如果还有字符输入的话,就不会执行requestMethods方法
    this.timeout = setTimeout(()=>{
      this.requestMethods()
    },500)
    //输入第一个字符的时候返回一个新的延迟函数,保存在this.timeout中,这个延迟函数在满足延迟时间后,会执行传requestMethods
//输入第二个字符的时候this.timeout已经有延迟函数(如果在500ms内),这时候把旧的延迟函数清除掉,再添加一个新的延迟函数
//输入第三个字符的时候重复如上步骤,直到最后一次输入,500ms延迟后,回调searchData方法
  },
  searchData(){
    this.debounce()
   //@input="searchData",每次输入字符都调用这个方法
  },
  requestMethods(){
//getData为请求方法
    getData(this.inputContent).then(res=>{
    this.inputSearch = res.data.inputSearch
})
  }
}

例子:

    // html 部分同上
    // js 部分
    let debounce = function (fn, wait) {
    let timeout = null;
    return function () {
      if (timeout !== null) clearTimeout(timeout);//如果多次触发将上次记录延迟清除掉
      timeout = setTimeout(() => {
        fn.apply(this, arguments);
        // 或者直接 fn()
        timeout = null;
      }, wait);
    };
    }
     // 处理函数
  function handle() {
    console.log(arguments)
    console.log(Math.random());
  }
  // 测试用例
  document.getElementsByClassName('scroll-box')[0].addEventListener("scroll", debounce(handle, 3000));

2.节流

高频事件触发,但在n秒内只会执行一次,如果n秒内再次触发事件,则直接return,所以节流会稀释函数的执行频率。

思路:每次触发事件时都判断当前是否有等待执行的延时函数

实例:

function throttle(fn) {
      let canRun = true; // 通过闭包保存一个标记
      return function () {
        if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
        canRun = false; // 立即设置为false
        setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
          fn.apply(this, arguments);
          // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
          canRun = true;
        }, 500);
      };
}
function sayHi(e) {
    console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));

//这边应用了函数柯里化,即利用函数执行可以形成一个不销毁的私有作用域(canRun),把预先处理的内容都存在这个不销毁的作用域里面(除了第一次外不再执行let canRun = true ),并且返回一个小函数,以后要执行的就是这个小函数。
//首先,return的函数会直接执行
//其次,当触发resize时,会直接执行return的函数,而没有再执行let canRun = true

例子:

 // html 部分
    <style>
    *{padding:0;margin:0;}
    .scroll-box{
        width : 100%;
        height : 500px;
        background:blue;
        overflow : auto;
    }    
    .scroll-item{
        height:1000px;
        width:100%;
    }
    </style>
    <body>
    <div class="scroll-box">
        <div class="scroll-item"></div>
    </div>
    </body>
    
    // js 部分
    let throttle = function (func, delay) {
    let timer = null;
    return function(){
      if (!timer) {
        timer = setTimeout(() => {
          func.apply(this, arguments);
          // 或者直接 func()
          timer = null;
        }, delay);
      }
    };
    };
      
      // 处理函数
      function handle() {
      console.log(arguments)
      console.log(Math.random());
      }
    // 测试用例
    document.getElementsByClassName('scroll-box')[0].addEventListener("scroll", throttle(handle,3000));

最后

走过路过,不要错过,点赞、收藏、评论三连~


驰骥
1 声望0 粉丝

认真、学习、思考、执行、耐心、主动