浅谈函数节流、函数防抖

229 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

在项目中应用过函数防抖、函数节流,但使用过就忘记了,说明并没有真正掌握,重新学习记录下。

防抖、节流是优化前端性能的常见方案之一,比如在滚动、鼠标的移动、搜索框输入等一些事件中,会频繁触发事件,造成浏览器性能问题,为了优化体验,需要限制事件的触发频率

防抖(debounce)

触发事件一段时间后,函数才执行;如果该段时间内事件再次被触发,则重新计算时间,函数在重新计算的该段时间后才执行

实现原理: 在执行目标函数时,会等待一段时间。当又触发事件执行相同函数时,若前一个定时任务未执行完,则清除掉定时任务,重新定时。

function debounce(fn, delay) {
      // 定义一个定时器, 保存上一次的定时器
      let timer;
      // 真正执行的函数
      return function () {
        //arguments 对象包含了传给函数的所有实参
        let args = arguments;
        // 取消上一次的定时器
        if (timer) clearTimeout(timer);
        // 延迟执行
        timer = setTimeout(() => {
          //this指向返回函数的this(表示调用的对象)
          fn.apply(this, args);
        }, delay)
      }
    }

疑问点: 返回的函数为什么不能使用箭头函数?

箭头函数的this指向最近的外层作用域中的this所指对象。这里返回函数如果为箭头函数,则this指向Window 对象。因为执行函数中可能会使用this相关的语句,因此用apply改变this指向,指向真正调用的对象

防抖的应用场景:

  1. 登录、发短信等按钮避免用户多次点击,重复提交
  2. 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多。只需窗口调整完成后,计算窗口大小。
  3. 轮播图切换

节流(throttle)

在规定的一段时间内,连续触发事件只执行一次。(比如控制人行道的绿灯每隔多长时间亮一次)

实现原理:

方式一:定时器

通过使用定时器,延迟方法执行,等时间到了再执行;在延时时间内,如果事件频繁触发,就直接return出去。

function throttle ( fn, interval) {
    let timer;
    //实际频繁触发的函数
    return function(){
       let args = arguments; 
       if (timer) {  return  }
       timer = setTimeout(() => {
          fn.apply(this, args);
          timer = null;
        }, interval)
    }
  }

方式二:时间戳

通过对比事件触发的时间差是否超过规定的间隔时间,如果是,就执行函数并更新上一次事件的执行时间

function throttle(fn, interval) {
    // 上一次事件触发的时间
    let lastTime = 0;
    return function() {
        // 当前事件触发的时间
        let args = arguments; 
        let nowTime = Date.now();
        if (nowTime - lastTime > interval) {
            fn.apply(this, args);
            // 更新上一次事件执行时间
            lastTime = nowTime;
        }
    }
}

节流的应用场景:

  1. scroll 事件,每隔一秒计算一次位置信息等。(之前写的有一篇文章实现图片懒加载,就需要在页面滚动触发事件时,给事件加上节流,让事件每隔500s执行一次,看看图片是否出现窗口,是否可以加载了)
  2. input 框实时搜索并发送请求,每隔一秒发送一次请求