防抖和节流

防抖和节流

概述

防抖和节流是前端开发中常用的函数优化手段,为了解决短时间内频繁触发某个功能函数而导致的性能问题。比如,触发频率过高而导致响应速度跟不上,以致出现延迟,假死或卡顿的现象。它们可以限制函数的执行频率,提升性能和用户体验。例如:用户的滚动、输入、点击和表单的重复提交等。

1. 防抖

1.1 是什么

当事件被触发 n 秒后,执行事件处理函数。若是在这 n 秒内,该事件又被触发,则重新计时,而不会执行事件处理函数。

它的原理是维护一个计时器,规定在 delay 时间之后触发回调,但是在 delay 时间内再次触发的话,就会取消之前的计时器而重新设置。

1.2 怎么用(如何实现)

const debounce = function (func, delay) {
  let timerId = null;
  return function() { 
    let context = this;
    let args = arguments;  
    timerId && clearTimeout(timerId);
    timerId = setTimeout(function() {
      func.apply(context, args);
    }, delay);
  }
}
let container = document.getElementById('container');
container.addEventListener('scroll', debounce(containerScroll, 1500));

1.3 何时用

  • 输入框搜索:当用户在搜索框中输入关键字时,使用防抖可以避免频繁发送搜索请求,而是在用户停止输入一段时间后才发送请求,减轻服务器压力。

  • 表单输入验证:表单输入过程中,每次用户输入都可能触发验证操作。使用防抖函数可以延迟触发验证操作,只在用户输入完毕一段时间后进行验证,避免频繁的验证操作。

  • 浏览器窗口调整事件:当用户调整浏览器窗口大小时,会触发 resize 事件。使用防抖函数可以延迟 resize 事件的触发,只在用户停止调整窗口一段时间后才执行对应的操作,避免频繁的计算和布局操作。

1.4 为什么 return 一个函数

  • 闭包的使用:闭包允许函数记住并访问其所在的词法作用域,即使函数在其词法作用域之外执行。在防抖函数中,外部函数返回一个内部函数,这个内部函数可以访问外部函数的变量和参数。这样就可以让 timer 变量不被回收,下次执行时仍然指向的是上一次设置的定时器。
  • 通过返回一个函数,你可以将这个防抖函数作为一个“工具”或“中间件”来使用,轻松地将它应用到任何需要防抖的函数上。你只需将需要防抖的函数作为参数传递给防抖函数,然后将返回的新函数用于事件监听或其他用途。

1.5 为什么要使用 apply

  • 因为 func 执行的时候 this 指向全局对象(浏览器中是window),根据词法作用域,可以在外层用个变量保存下 this ,通过 apply 可以修正 this 指向以及传递正确的参数 arguments。

2. 节流

2.1 是什么

当持续性触发事件时,确保一定时间段内只会调用一次事件处理函数。其实现原理:是通过判断是否到达一定时间来触发回调函数。

2.2 如何实现

2.2.1 时间戳版

当触发事件的时候,获取当前的时间戳,然后减去之前的时间戳, 如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行

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

特点:事件触发的时候立刻执行一次,停止触发后,不会再执行事件。

2.2.2 定时器版

当触发事件的时候,设置一个定时器, 后续再触发事件的时候, 判断定时器如果存在,就不执行, 直到上一个定时器执行后,才可以设置下个定时器

  function throttle(func, wait) {
    let context = null;
    let args = null;
    let timeout = null;
    return function() {
      context = this;
      args = arguments;
      if (!timeout) {
        timeout = setTimeout(function() {
          timeout = null;
          func.apply(context, args);
        }, wait);
      }
    }
  }

特点:事件触发的时候不会立刻执行,停止触发后,会再执行一次事件。

2.2.3 时间戳 + 定时器
function throttle(func, wait) {
  let context = null; 
  let args = null;
  let timeout = null;
  let previous = 0;

  let later = function() {
    previous = +new Date();
    timeout = null;
    func.apply(context, args);
  };

  let throttled = function() {
    let now = +new Date();
    // 下次触发 func 剩余的时间·
    let remaining = wait - (now - previous);
    context = this;
    args = arguments;
    if (remaining <= 0) {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      previous = now;
      func.apply(context, args);
    } else if (!timeout) {
      timeout = setTimeout(later, remaining);
    }
  };
  return throttled;
}

特点:事件触发的时候会立刻执行一次,停止触发的时候还会执行一次

2.3 何时用

  • 页面滚动:当页面滚动时,使用节流可以限制滚动事件的触发频率,减少事件处理的次数,提高页面的响应性能。
  • 拖拽场景: 在某些场景下,频繁触发位置变动会造成性能问题,固定时间内只执行一次,防止超高频次触发位置变动。

3.总结

  • 函数防抖:当事件被触发 n 秒后,执行事件处理函数。若是在这 n 秒内,该事件又被触发,则重新计时,而不会执行事件处理函数;
  • 函数节流:当持续性触发事件时,确保一定时间段内只会调用一次事件处理函数;
  • 区别:函数节流不管事件触发有多频繁,都会保证在规定时间内执行一次事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数;所以如何选择需要根据具体的业务需求。
  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值