跳至主要內容

防抖节流

哓番茄大约 2 分钟JavascripthandlewriteInterview

防抖节流

函数节流和防抖

函数节流和防抖都是优化频繁执行的事件,它们的区别就是是否过程中需要函数返回获取结果,节流,超过设定时间才会执行一次,在执行过程中会需要函数执行. 防抖,事件最后一次执行

节流

n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

  • 滚动加载,加载更多或滚到底部监听
  • 搜索框,搜索联想功能
// 简单版节流
function throttle(fn, delay) {
  let oldtime = Date.now()

  return function (...args) {
    let newtime = Date.now()
    if (newtime - oldtime >= delay) {
      fn.apply(null, args)
      oldtime = Date.now()
    }
  }
}

function throttled(fn, delay) {
  let timer = null
  let starttime = Date.now()
  return function () {
    let curTime = Date.now() // 当前时间
    // 从上一次到现在,还剩下多少多余时间
    let remaining = delay - (curTime - starttime)
    let context = this
    let args = arguments
    clearTimeout(timer)
    if (remaining <= 0) {
      fn.apply(context, args)
      starttime = Date.now()
    } else {
      timer = setTimeout(fn, remaining)
    }
  }
}

CSS 节流

css 节流通过使用pointer-events禁用点击事件, 然后通过animation结合:actived通过动画效果设置在规定时间内,元素不可被点击, 节流时间通过设置动画效果控制。

点击以下示例,打开控制台,节流时间设置的两秒,console 打印结果

button {
  animation: throttle 2s step-end forwards;
}
button:active {
  animation: none;
}
@keyframes throttle {
  from {
    pointer-events: none;
  }
  to {
    pointer-events: all;
  }
}

防抖

n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测
  • 窗口大小 resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
// 简单版防抖
function fangdou(fns, delay) {
  let timer
  return function () {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      fns()
    }, delay)
  }
  // return function() {
  // 	const context = this
  // 	const arg = arguments

  // 	if (timer) clearTimeout(timer)
  // 	timer = setTimeout(function(){
  // 		fns.apply(context, arg)
  // 	},delay)
  // }
}

function debounce(func, wait, immediate) {
  let timeout
  return function () {
    let context = this
    let args = arguments

    if (timeout) clearTimeout(timeout) // timeout 不为null
    if (immediate) {
      let callNow = !timeout // 第一次会立即执行,以后只有事件执行后才会再次触发
      timeout = setTimeout(function () {
        timeout = null
      }, wait)
      if (callNow) {
        func.apply(context, args)
      }
    } else {
      timeout = setTimeout(function () {
        func.apply(context, args)
      }, wait)
    }
  }
}