js 函数防抖与节流

前言

我们为什么需要函数防抖与节流

我们在实际开发当中,可定会遇见持续触发的事件如:onmousemoveonchangeresize等这种持续触发的事件,我们还可能会遇见那么持续需要点击的功能:抽奖点击购买商品等功能

以上这种情况都会引出来一个问题,那就是页面可能会卡顿,性能较低等问题

那么我们可以通过函数节流和函数防抖等我们人为定义函数功能的方式来解决这种问题

什么是函数防抖与节流

举个栗子,当 1 秒内连续播放 24 张以上的图片时,在人眼的视觉中就会形成一个连贯的动画,所以在电影的播放中基本是以每秒 24 张的速度播放的,为什么不 100 张或更多是因为 24 张就可以满足人类视觉需求的时候,100 张就会显得很浪费资源

再举个栗子,假设电梯一次只能载一人的话,10 个人要上楼的话电梯就得走 10 次,是一种浪费资源的行为;而实际生活正显然不是这样的,当电梯里有人准备上楼的时候如果外面又有人按电梯的话,电梯会再次打开直到满载位置,从电梯的角度来说,这时一种节约资源的行为(相对于一次只能载一个人)。

  • 函数节流: 指定时间间隔内只会执行一次任务;
  • 函数防抖: 任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。

函数节流的核心是,让一个函数不要执行得太频繁,减少一些过快的调用来节流。
函数去抖就是对于一定时间段的连续的函数调用,只让其执行一次。

应用场景

函数节流(throttle)应用场景

函数节流有哪些应用场景?哪些时候我们需要间隔一定时间触发回调来控制函数调用频率?

  • DOM 元素的拖拽功能实现(mousemove
  • 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
  • 计算鼠标移动的距离(mousemove
  • Canvas 模拟画板功能(mousemove
  • 搜索联想(keyup

函数防抖(debounce)应用场景

函数去抖有哪些应用场景?哪些时候对于连续的事件响应我们只需要执行一次回调?

  • 每次 resize/scroll 触发统计事件
  • 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)

js 函数防抖与节流_第1张图片

函数节流和函数防抖的核心其实就是限制某一个方法被频繁触发,而一个方法之所以会被频繁触发,大多数情况下是因为 DOM 事件的监听回调,而这也是函数节流以及防抖多数情况下的应用场景。

实现

实现函数防抖

<script>
  /*
   *  函数防抖
   *   在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。
   **/
   // technique防抖函数:接收的形参 fn函数 time时间
   function technique(fn, time) {
      let resultTime = null // 定时器的返回值(用于清除定时器)
      return function (...args) { // 返回一个函数并且扩展出一些参数
          clearTimeout(resultTime) // 清除定时器
          // 执行定时器 并且该变fn函数的this指向和设置time的时间
          resultTime = setTimeout(fn.bind(this, ...args), time)
      }
   }
   // 使用
   const resultFn = technique(function (e) {
         console.log('执行', this, e);
   }, 1000)
   document.addEventListener('click', resultFn)
</script>

代码解读

  • 首先我们人为定义一个technique的函数,并且接收两个参数(函数(fn)执行的时间(time)
  • 我们在初始的时候要设置一个变量resultTime来接收我们定时器返回出来的值,用于我们清除上一次定时器
  • 并且我们要return返回出来一个函数并且用扩展运算符来扩展出来一下参数(如事件对象,我们传递过来的参数)
  • 并且我们在执行technique函数的时候需要清除一下我们上一次开启的定时器,并且在开启一个定时器,还需要用bind改变fn函数的this指向问题和传递参数的问题
  • 然后我们就可以在事件当中去应用这个technique防抖函数了

实现函数节流

<script>
    /*
    *  函数节流
    *       连续执行函数,每隔一定时间执行函数。规定一个单位时间,
    *       在这个单位时间内,只能有一次触发事件的回调函数执行,
    *       如果在同一个单位时间内某事件被触发多次,只有一次能生效。
    * */
    // throttle节流函数 接收的参数:fn函数,time时间
    function throttle(fn, time) {
        let start = 0 // 初始函数节流的时间
        return function (...args) { // 返回一个函数并且扩展出一些参数
            const newDate = new Date()  // 获取当前最新的时间
            // 如果最新的时间-节流的时间 >= time我们人为定义节流的时间 那么就执行
            if (newDate - start >= time) {
                start = new Date() // 从新设置初始节流的时间
                fn.call(this, ...args) // 修改this指向并且传递一些参数
            }
        }
    }
    // 应用
    const resultFn = throttle(function (e) {
        console.log('执行', this, e);
    }, 1000)
    document.addEventListener('click', resultFn)
</script>

代码解读

  • 我们先人为定义throttle的函数,并且接收两个参数(函数(fn)执行的时间(time)
  • 我们在定义start变量,用于记录初始的节流时间默认是0即可
  • 我们在返回出去一个函数,并且这个函数的形参需要扩展出来一下参数...args,而在这个函数内部我们在定义一个当前最新的时间变量newDate
  • 在进行if的判断:如果最新的时间(newDate)-节流的时间(start) >= 我们人为定义节流的时间(time),那么我们就从新设置节流的时间start,并且改变fn函数的this执行和传递一些参数
  • 然后我们就可以在事件当中去应用这个throttle节流函数了

应用

应用节流函数

css+html代码

<style>
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    .box {
        position: absolute;
        top: 0;
        left: 0;
        width: 200px;
        height: 200px;
        background-color: pink;
        cursor: pointer;
    }
style>
<div class="box">div>

javascript代码

var oBox = document.getElementsByClassName("box")[0]
// 点鼠标点击盒子的时候 触发mousedown事件
oBox.addEventListener('mousedown', function (e) {
    // 并且获取他的clientX和Y值
    var clientX = e.clientX
    var clientY = e.clientY
    // 并且获取他的offsetTop和offsetLeft值
    var offsetTop = this.offsetTop
    var offsetLeft = this.offsetLeft
    // 当鼠标移动的时候触发mousemove事件
    function move(e) {
        // 并且获取他的clientY和X值
        var moveX = e.clientX
        var moveY = e.clientY
        // 并且改变他的left和top值
        oBox.style.left = offsetLeft + moveX - clientX + 'px'
        oBox.style.top = offsetTop + moveY - clientY + 'px'
    }
    // mousemove事件 函数节流 并且传递move函数
    function throttle (fn, time = 100) {
        var start = 0; // 初始节流的时间
        return function (...args) {
            var now = new Date
            if (now - start >= time) {
                fn.call(this, ...args)
                start = now
            }
        }
    }
    var throttleFn = throttle(move, 80)
    // 把函数节流的函数作为mousemove的事件处理函数
    document.addEventListener('mousemove', throttleFn)
    // 当鼠标抬起的时候 清除事件
    oBox.addEventListener('mouseup', function () {
        document.removeEventListener('mousemove', throttleFn)
        oBox.removeEventListener('mouseup', oBox)
    })
})

上面代码我们要实现一个div盒子拖动的效果,我们用到了onmousemove时间,而这个事件是多次触发的一个事件,我们可以用节流函数来优化他,并且我们人为可以设置他的拖拽时间

应用防抖函数

react-hooks代码

// 当触发input框当中的change事件的时候触发该事件处理函数
const handleChange = e => {
    const val = e.target.value
    const result = technique(function () {
        setVal(val) // 设置对应的val值
        getQueryData(val) // 请求对应的数据
        if (!val) { // 如果val值为空那么我们就发送一个[]数组 代表没有获取到数据
            Pubsub.publish('SongList', [])
        }
    }, 20)
    result()
}

technique防抖函数

// 防抖函数
export function technique (fn, time = 1000) {
    let resultTime = null
    return function (...args) {
        clearTimeout(resultTime)
        resultTime = setTimeout(fn.bind(this, ...args), time)
    }
}

这段代码是我在一个react hook项目上面拿到的一段代码,大概的功能是我们输入input框的时候需要使用onchange来实时监测他的value值的变化,并且想后台发起请求来获取对应的歌曲数据,在这个功能上面我们使用了防抖函数来优化他,让他在我们认为定义的时间内去获取歌曲数据的请求

小结

节流和防抖函数主要适用于高频触发的操作,如果对于高频触发的时间我们不加以限制,会导致频繁的页面渲染和或者请求等。影响用户体验,此时我们可以用debounce()throttle()的方式来减少回调被处罚的频率,这样既可以达到我们想要的效果又可以减弱对用户体验的影响。

你可能感兴趣的:(JavaScript,js,javascript)