前端性能优化:防抖节流

162 阅读6分钟

前言

经历过多场面试后,总能间接或直接地被面试官问到关于防抖和节流甚至要手写出代码来!!!。为此呢决定好好写篇文章来总结一下!!以便面试官下次问道的时候能回答的更好!!

a013f2ceacbf1df9d47779aa85d9760.jpg

闭包

其实在防抖节流中都运用到了闭包这一概念。为此我们可以先从闭包讲起! 概念:闭包是一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域。闭包就是内层函数+外层函数的变量(一个函数嵌套另一个函数的时候,内层函数和外层函数的变量构成了闭包)。看看代码吧!

function outer(){
  const a=1
  function f(){
      console.log(a)
  }
   return f
}
const fun =outer()
 fun()  //打印出来了10

值得注意的是:函数套用函数其实不会产生闭包,在此基础上里层函数用到了外层函数的变量时候才会产生。从fun打印出来了10可以看出一个函数访问了另外一个函数内部的值。

闭包的应用

    let i=0
   function fn(){
     i++
  console.log(`函数被调用了${i}次`)
}

我们可以不断的使用fn()来进行调用,可以得到次数。但很明显这样会容易出现问题,我们所申明的i是一个全局变量,这意味着谁都可以使用容易被修改,容易受到污染。因此我们可以用到闭包来解决。1.使得数据私有化不被污染。闭包可以用来创建私有变量和方法。由于外部不可直接访问内部的变量,因此我们可以通过闭包来模拟私有成员,确保这些成员不会被意外修改。2.闭包可以帮助实现记忆功能,即缓存函数的结果以便后续调用时可以直接返回之前计算过的结果,而无需重新计算。 我们使用闭包来看看效果吧!!

  function count(){
      let i=0
      function fn(){
         i++
         console.log(i)
      }
      return fn
  }
  const fun=count()
  fun()//1
  fun()//2
  fun()//3

内存泄漏

函数一旦被执行完毕,其内存就会被销毁。由于闭包会引用外部函数的变量,但是这些变量在外部函数执行完毕后没有被释放,那么这些变量会一直存在于内存中,总的内存大小不变,但是可用内存空间变小了。 一旦形成闭包,只有在页面关闭后,闭包占用的内存才会被回收,这就造成了所谓的内存泄漏。如之前的代码我们可以看到fun是全局变量只有当页面关闭的时候才会被释放,而fun指向fn,而fn又能访问到i。由此可见不会被释放,这也就带来了内存泄露。

防抖

定义:单位时间内,频繁触发但是只会执行最后一次。 原理:防抖通过设置一个定时器来实现。当事件首次触发时,设定一个延迟时间,在这个延迟时间内如果再次触发事件,则重置定时器。只有当事件停止触发并且超过了设定的延迟时间后,才会执行一次处理函数。 有些场景需要进行防抖优化处理:搜索框输入、表单验证、文件上传、按钮点击 (click),手机号、邮箱验证输入检测、窗口调整大小、滚动事件 (scroll)。 防抖的优点:-

  • 减少不必要的计算:避免频繁的DOM操作、网络请求等。
  • 减少网络请求:降低服务器负载,减少不必要的网络流量。
  • 提升用户体验:确保应用程序更加流畅和响应迅速。
  • 优化资源管理:更好地管理内存和其他资源,避免泄露。
  • 简化代码逻辑:减少代码的复杂性和维护成本。
  • 防止重复操作:避免用户无意中触发多次相同的操作。
  • 提高性能:特别是在移动设备或低性能设备上,显著提升应用程序的性能。

话不多说我们书写一个防抖函数来看看吧!!

     const box =document.querSlector('.box')
     let i=1
     function mouseMove(){
       box.innerHTML=i++
         //如果里边存在了大量消耗性能的代码,比如dom操作,比如数据处理,可能造成卡顿
     }
     function debounce(fn,t){
          let timer
          return function(){
             if(timer)clearTimeout(timer)
           timer=setTimeout(function(){
               fn()
             },t)
          }
     }
     box.addEventListener('mousemove',debounce(mouseMove,500))

一句话总结:手写防抖函数的核心是利用setTimeout定时器来实现的。有如下的步骤: 1.声明定时器变量。2.每次鼠标移动(事件触发)的时候都要先判断是否有定时器,如果有的话则先清除以前的定时器。3.如果没有定时器则开启定时器,存入定时器变量里边。4.定时器里边写函数调用

节流

定义:单位时间内,频繁触发事件,只执行一次。节流(Throttling)是另一种用于优化频繁触发事件的技术,它通过限制函数在一定时间间隔内最多执行一次来减少不必要的计算和资源消耗,节流不会等到事件停止触发后再执行处理函数,而是在设定的时间间隔内定期执行。 节流场景:

  • 滚动事件:定期更新视口内的元素或加载更多内容。
  • 窗口调整大小:定期执行复杂的布局更新或重绘。
  • 输入框搜索:定期发起搜索请求或执行复杂的搜索逻辑。
  • 拖拽操作:定期更新拖拽状态或保存位置。
  • 游戏开发中的用户输入:定期处理用户输入,提高游戏的流畅度和响应速度。
  • 动画效果:定期更新动画状态,提高性能。
  • 地图缩放和平移:定期更新地图数据或重绘,减少不必要的计算。
  • 表单验证:定期进行验证,避免过早或过多的验证提示。
  • 自动保存:定期自动保存,减少服务器负载。
  • 文件上传:定期处理文件选择,避免频繁的上传请求。
  • 语音识别:定期处理识别结果,提高准确性和减少不必要的处理。
  • 鼠标移动:定期处理鼠标移动,提高性能并减少不必要的计算。 话不多说我们来写段节流的代码看看吧!!
const box=document.querySelector('.box')
let i=1
function mouseMove(){
  box.innerHTML=i++
   // 如果里边存在了大量消耗性能的代码,比如dom操作,比如数据处理,可能造成卡顿
}
 function throttle(fn,t){
   let timer=null
   return function(){
      if(!timer){
       timer=setTimeout(function(){
       fn()
       timer=null
       },t)
      }
   }
 }
 box.addEventListener('mousemove',throttle(mouseMove,3000))

总结:节流的核心就是利用定时器setTimeout来实现。1.声明一个定时器变量 2.当每次鼠标移动(事件触发)都先判断是否有定时器了,如果有定时器了则不启新的定时器 3.如果没有定时器则开启定时器,存到变量里边。定时器里边调用执行的函数,定时器里边要把定时器清空。 好啦今天的分享就到这里啦!请多多关注哈~~