Js 函数防抖和节流

咱们前端在实际开发过程当中,常常会遇到以下问题:前端

  • 页面的 scroll 事件
  • input 输入检测事件
  • 高频点击提交

若是不作任何处理的话,页面可能会卡顿,性能较低。这时候就须要函数防抖和节流来出马啦。bash

1. 函数防抖

1.1 定义

在事件被触发n秒后再执行回调,若是在这n秒内又被触发,则从新计时。app

例子:电梯是在等人进入10秒后自动关闭。若是电梯进人后10s内再次有人进入,则又得等10秒钟电梯才会自动关闭。若是中间一直进人,就一直不会关闭。函数

1.2 代码实现

思路:用定时器执行函数,判断若是存在定时器任务,则清空定时器,从新开启一个新的定时器性能

简易代码:ui

// fn 是须要执行的函数, interval 是等待时间间隔
function debounce(fn, interval = 300) {
  let timeout = null
  return function() {
    clearTimeout(timeout)
    timeout = setTimeout(()=> {
      fn.apply(this, arguments)
    }, interval)
  }
}
复制代码

可是这一版防抖有个缺点,就是第一次不会执行,得等到 interval 后才会执行,若是须要首次执行,可修改代码以下:this

// immediate 为 true 表明须要当即执行 fn, 为 false 表明不须要
function debounce(fn, interval = 1000, immediate) {
  let timeout = null
  let first = true
  return function() {
    clearTimeout(timeout)
    if (immediate && first) {
      fn.apply(this, arguments)
      first = false
    }
    timeout = setTimeout(()=> {
      fn.apply(this, arguments)
    }, interval)
  }
}
复制代码

1.3 使用

document.querySelector('body').addEventListener('mousemove', debounce((e) => {
  console.log(e.clientY)
}, 300, true))
复制代码

如图,对 mousemove事件防抖:spa

debounce

2. 函数节流

2.1 定义

规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,若是在同一个单位时间内某事件被触发屡次,只有一次能生效。3d

例子: 在页面滚动时,会不断地触发 scroll 事件,实际上咱们不须要这么频繁地去执行函数,这是就能够设置一个间隔,好比说 300ms,在 300ms 这个时间内,scroll 内的函数只能执行一次。code

2.2 代码实现

思路 1:用时间轴来判断,若是事件触发时间与上一次函数执行时间之差大于规定时间,则执行一次,反之不执行

代码 1:

// fn 是须要执行的函数, interval 是等待时间间隔
function throttle(fn, interval = 1000) {
  let pre = 0
  return function() {
    const now = Date.now()
    if (now - pre > interval) {
      fn.apply(this, arguments)
      pre = now
    }
  }
}
复制代码

这个函数特色是:会马上执行,中止触发后就不会执行。

思路 2:用定时器,在定时器未执行的时间内,再次触发的函数将再也不执行。

代码 2:

// fn 是须要执行的函数, interval 是等待时间间隔
function throttle(fn, interval = 1000) {
  let canRun = true
  return function() {
    if (!canRun) {
      return
    }
    canRun = false
    setTimeout(() => {
      fn.apply(this, arguments)
      canRun = true
    }, interval)
  } 
}
复制代码

这个函数特色是:等待 interval 时间间隔后才执行,中止触发后会再执行一次。

把思路 1 和思路 2 结合起来,用变量来控制第一次是否执行及中止触发后是否再执行一次:

// options 做为第三个参数,
// options.leading 为 true 时表示第一次就执行,
// options.trailing 为 true 是表示中止触发时再执行一次

function throttle(fn, interval = 1000, options = {}) {
  let pre = 0
  let canRun = true
  let first = options.leading || false
  return function() {
    if (first) {
      fn.apply(this, arguments)
        first = false
    }
    if (options.trailing) {
      if (!canRun) {
        return
      }
      canRun = false
      setTimeout(() => {
        fn.apply(this, arguments)
        canRun = true
      }, interval)
    } else {
      const now = Date.now()
      if (now - pre > interval) {
        if (pre) {
          fn.apply(this, arguments)
        }
        pre = now
      }
    }
  }
}
复制代码

2.3 使用

document.querySelector('body').addEventListener('mousemove', throttle((e) => {
  console.log(e.clientY)
}, 1000, {
  leading: true,
  trailing: true
}))
复制代码

如图,对 mousemove事件节流:

throttle
相关文章
相关标签/搜索