• 防抖和节流


    防抖和节流

    防抖和节流

     

    > 更多文章详见公众号【前端css和js干货】

    1.debounce(防抖)和throttle(节流)的定义

    口语版:
    防抖就是只有当小明连续10天不捣蛋时,小明爸爸才给他零花钱。如果在这10天内小明捣蛋了, 那么重新计算,直到满足了10天不捣蛋的条件,小明爸爸才给零花钱。一年下来小明居然只拿到了5次零花钱,你说气人不?
    节流就是无论小明捣蛋不捣蛋,小明爸爸每隔10天都给小明零花钱。一年下来,小明拿到了36次零花钱。
    防抖是有条件的周期动作,而节流是没有条件的周期动作。
    书面版:
    防抖:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。
    节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。

    2.防抖和节流解决什么问题

    在进行resize、scroll、keyup、keydown、mousedown、mousemove等事件操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,容易导致页面卡顿等影响用户的体验;这时就可以通过debounce(防抖)和throttle(节流)函数来限制事件处理函数的调用频率,提升用户的体验,同时又不影响实际的效果。

    3.防抖和节流的应用场景

    防抖
    1.登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖。
    2.调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多。
    3.文本编辑器实时保存,当无任何更改操作一秒后进行保存。
    4.DOM 元素的拖拽功能实现。
    5.计算鼠标移动的距离。
    6.Canvas 模拟画板功能。
    7.搜索联想。
    节流
    1.scroll 事件,每隔一秒计算一次位置信息等。
    2.浏览器播放事件,每个一秒计算一次进度信息等。
    3.input框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖)。

    4.防抖的代码实现

    以scroll事件为例

    function scrollHandler() {
      console.log('滚动了')
    }
    window.addEventListener('scroll', scrollHandler)
    

    效果如下,触发了很多次回调事件:

    1.防抖函数的基本实现:

    function debounce(fn, wait) {
      var timeout  //闭包的方式定义一个全局变量,是实现防抖函数的核心
      return function () {
        var context = this,
        args = arguments
        clearTimeout(timeout)
    
        timeout = setTimeout(function () {
          fn.apply(context, args)
        }, wait)
      }
    }
    window.addEventListener('scroll', debounce(scrollHandler, 500))
    

    理解的难点:
    a)逻辑过程是, 定义一个setTimeout事件, 假如在触发之前,又发生了scroll事件的话, 通过clearTimeout函数清除之前定义的setTimeout事件,同时又再次定义一个setTimeout定时事件,从而达到防抖的功能。
    b) debounce(scrollHandler, 500),这句代码返回的是一个函数,并没有显式传递参数进去,为什么还要特意用args = arguments这句话特意的接受一下参数, 原因是因为js引擎会自动的传递event事件参数,所以用args = arguments这句话是为了接受event等默认传递的参数。
    2.立即执行的版本的防抖函数,刚才的防抖函数是没有立即执行的,也就是触发事件后不会马上执行,但是某些场景下需要立即执行;立即执行后当n秒内触发事件才能再次执行。

    function debounce(fn, wait) {
      let timeout, result;
      return function () {
        const context = this
        const args = arguments
        clearTimeout(timeout)
        const callNow = !timeout
        timeout = setTimeout(function() {
          timeout = null
        }, wait)
        if (callNow) result = fn.apply(context, args)
        return result
      }
    }
    

    理解了上个版本的基础上,再来理解立即执行版本的防抖函数,应该比较简单,核心就是通过callNow变量及setTimeout函数把timeout置空的方式来决定是否调用回调函数。
    3.综合版本,为了更加灵活的使用,适应各种场景。

    function debounce(fn, wait, immediate) {
      var timeout, result;
      return function () {
        var context = this
        var args = arguments
        clearTimeout(timeout)
        if (immediate) {
          var callNow = !timeout
          timeout = setTimeout(function () {
            timeout = null
          }, wait)
          if (callNow) result = fn.apply(context, args)
        } else {
          timeout = setTimeout(function () {
            fn.apply(context, args)
          }, wait)
        }
        return result
      }
    }
    

    5.节流的代码实现

    1.节流函数的基本实现

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


    基本的逻辑是对timeout进行判断, 只有为空才能设置setTimeout事件,从而达到了一个周期只执行一次回调函数的功能,不过这个函数,并没有立即执行的功能。
    2.立即执行的版本

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

    previous初始为0, now-previous肯定大于wait,所以fn函数在一开始就会被触发。一个wait周期之后又会重新触发。
    3.综合版

    function throttle(fn, wait, immediate) {
      let timeout
      let previous = 0
       reutrn function () {
        let context = this
        let args = arguments
        if (immediate) {
          let now = Date.now()
          if (now - previous > wait) {
            fn.apply(context, args)
            previous = now
          }
        } else {
          if (!timeout) {
            timeout = setTimeout(() => {
              timeout = null
              fn.apply(context, args)
            }, wait)
          }
        }
      }
    }
    

    6.总结

    函数节流与函数防抖都是为了限制函数的执行频次,都是一种性能优化的方法。区别是防抖是有条件的周期性动作,而节流是无条件的周期性动作。两者实现的核心都依赖于闭包。

  • 相关阅读:
    截屏 多难未遂
    捕捉异常
    Android中缓存记忆
    Android中的线程池
    悄悄为Android中解决部分适配问题哦!
    java中的服务端跳转与客户端跳转区别与联系
    doget(),doput()方法的使用
    基本概念--同步,异步
    java--SimpleDataFormat类
    java--9
  • 原文地址:https://www.cnblogs.com/sexintercourse/p/16541698.html
Copyright © 2020-2023  润新知