JavaScript 防抖和节流详解

 更新时间:2021年09月27日 16:44:54   作者:小啊菜啊  
这篇文章主要介绍了JavaScript 防抖和节流,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

防抖

自动门感应到有人,打开门,并且开始5秒倒计时,在 5 s 内有另外一个人靠近到门,门感应到人,重新5秒倒计时

当事件被触发时,设定一个延迟,若期间事件又被触发,则重新设定延迟,直到延迟结束,执行动作 (防止多次触发)

web 应用上面

  • 改变页面大小的统计
  • 滚动页面位置的统计
  • 输入框连续输入的请求次数控制

一开始,点击按钮,console.log('pay money')

<body>
  <button id="btn">click</button>
</body>
<script>
  const btn = document.getElementById('btn')
  function payMoney() {
    console.log('pay money');
  }
  btn.addEventListener('click', payMoney)
</script>

定义 debounce

const btn = document.getElementById('btn')
function payMoney() {
  console.log('pay money');
}
function debounce(func) {
  // 在函数里面返回函数 , 只有当点击的时候才返回该函数
  return function () {
    func()
  }
}
btn.addEventListener('click', debounce(payMoney))

设置延时

const btn = document.getElementById('btn')
function payMoney() {
  console.log('pay money');
}
function debounce(func, delay) {
  return function () {
    setTimeout(_ => func(), delay)
  }
}
btn.addEventListener('click', debounce(payMoney, 1000))

清除延时:未能执行

原因

每次点击的时候就会执行返回函数里面的内容

每次点击的执行函数都是独立的,互不干涉

正因为他们之间没有联系,清除延时在这里完全没有起作用

要让这些独立的执行函数之间有联系,需要用到作用域链(闭包)

const btn = document.getElementById('btn')
function payMoney() {
  console.log('pay money');
}
function debounce(func, delay) {
  return function () {
    let timer
    clearInterval(timer)
    timer = setTimeout(_ => func(), delay)
  }
}
btn.addEventListener('click', debounce(payMoney, 1000))

将 timer 放在返回函数的外围,这样在定义监听事件的同时,就定义了 timer变量

因为作用域链,所有独立的执行函数都能访问到这个timer变量

而且现在这个timer变量只创建了一次。是唯一的,我们只不过不断给timer赋值进行延时而已

每个清除延时就是清除上一个定义的延时,相当于多个函数共用同一个外部变量

const btn = document.getElementById('btn')
function payMoney() {
  console.log('pay money');
}
function debounce(func, delay) {
  let timer
  return function () {
    clearInterval(timer)
    timer = setTimeout(_ => func(), delay)
  }
}
btn.addEventListener('click', debounce(payMoney, 1000))

此时的代码,this 是指向 window ?

因为回调的原因,运行时已经在Window下了

const btn = document.getElementById('btn')
function payMoney() {
  console.log('pay money');
  console.log(this);
}
function debounce(func, delay) {
  let timer
  return function () {
    clearInterval(timer)
    timer = setTimeout(_ => func(), delay)
  }
}
btn.addEventListener('click', debounce(payMoney, 1000))

解决办法

在 setTimeout 之前将 this 保存下来,此时的 this 是指向按钮的

const btn = document.getElementById('btn')
function payMoney() {
  console.log('pay money');
  console.log(this);
}
function debounce(func, delay) {
  let timer
  // 只有当点击的时候,才返回此函数,所以 this 是指向按钮的
  return function () {
    let context = this
    clearInterval(timer)
    timer = setTimeout(_ => {
      func.apply(context)
    }, delay)
  }
}
btn.addEventListener('click', debounce(payMoney, 1000))

考虑参数的问题,添加 arg

const btn = document.getElementById('btn')
function payMoney() {
  console.log('pay money');
  console.log(this);
}
function debounce(func, delay) {
  let timer
  return function () {
    let context = this
    let args = arguments
    clearInterval(timer)
    timer = setTimeout(_ => {
      func.apply(context)
      console.log(context, args);
    }, delay)
  }
}
btn.addEventListener('click', debounce(payMoney, 1000))

节流

先触发一次后,防止接下来多次触发

滚动屏幕:统计用户滚动屏幕的行为来作出相应的网页反应

当用户不断进行滚动,就会不断产生请求,相应也会不断增加,容易导致​ ⛔️ 网络阻塞

我们就可以在触发事件的时候就马上执行任务,然后设定时间间隔限制,在这段时间内不管用户如何进行滚动都忽视操作

在时间到了以后如果监测到用户有滚动行为,再次执行任务。并且设置时间闻隔

首先,写个改变页面尺寸的同时改变页面背景颜色的代码

function coloring() {
  let r = Math.floor(Math.random() * 255)
  let g = Math.floor(Math.random() * 255)
  let b = Math.floor(Math.random() * 255)
  document.body.style.background = `rgb(${r}, ${g}, ${b})`
}
window.addEventListener('resize', coloring)
function throttle(func, delay) {
  let timer
  return function () {
    timer = setTimeout(_ => {
      func()
    }, delay)
  }
}
window.addEventListener('resize', throttle(coloring, 2000))

判断触发的事件是否在时间间隔内

  • 不在:触发事件
  • 在:不触发事件
function throttle(func, delay) {
  let timer
  return function () {
    // timer 被赋值了,直接返回,即不执行任务
    if (timer) {
      return
    }
    // 此时 timer 没被赋值,或 timer 已经执行完了  
    // 为 timer 赋值进行延时执行
    timer = setTimeout(_ => {
      func()
      // 延迟执行以后我们要清空timer的值
      timer = null
    }, delay)
  }
}
window.addEventListener('resize', throttle(coloring, 2000))

解决 this 指向(虽然当前的这个例子就是在 Window 下的)

function throttle(func, delay) {
  let timer
  return function () {
    let context = this
    let args = arguments
    // timer 被赋值了,直接返回,即不执行任务
    if (timer) {
      return
    }
    // 此时 timer 没被赋值,或 timer 已经执行完了  
    // 为 timer 赋值进行延时执行
    timer = setTimeout(_ => {
      func.apply(context, args)
      // 延迟执行以后我们要清空timer的值
      timer = null
    }, delay)
  }
}
window.addEventListener('resize', throttle(coloring, 1000))

节流核心:事件间隔 另一种常见的时间间隔就是用Date对象

function throttle(func, delay) {
  // 我们要和前一个时间点进行比较才能确定是否已经过了时间间隔
  // 在返回函数外围,避免每次执行都被自动修改
  let pre = 0
  return function () {
    // 保存执行函数当时的时间
    let now = new Date()
    // 刚开始,一定会执行
    if (now - pre > delay) {
      // 已经过了时间间隔,就可以执行函数了
      func()
      // 执行后,重新设置间隔点
      pre = now
    }
  }
}
window.addEventListener('resize', throttle(coloring, 1000))

解决参数问题

function throttle(func, delay) {
  let pre = 0
  return function () {
    let context = this
    let args = arguments
    let now = new Date()
    if (now - pre > delay) {
      func.apply(context, args)
      pre = now
    }
  }
}
window.addEventListener('resize', throttle(coloring, 1000))

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • JS实现为表格动态添加标题的方法

    JS实现为表格动态添加标题的方法

    这篇文章主要介绍了JS实现为表格动态添加标题的方法,涉及javascript中createCaption方法添加标题的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • js实现可旋转的立方体模型

    js实现可旋转的立方体模型

    这里给大家分享的是通过js脚本来控制页面中的正方体转动特效,用户可以点击按钮向右转动,也可以向下转动,结合自己的需求控制即可。效果非常棒,这里推荐给大家
    2016-10-10
  • 微信小程序实现发红包功能

    微信小程序实现发红包功能

    这篇文章主要为大家详细介绍了微信小程序实现发红包功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-07-07
  • js apply/call/caller/callee/bind使用方法与区别分析

    js apply/call/caller/callee/bind使用方法与区别分析

    js apply/call/caller/callee/bind使用方法与区别分析,需要的朋友可以参考下。
    2009-10-10
  • text-align:justify实现文本两端对齐 兼容IE

    text-align:justify实现文本两端对齐 兼容IE

    对于text-align 我们再熟悉不过了,可是它有个justify属性,平时很少用到,就鲜为人知了。justify是一种文本靠两边布局方式,一般应用于书刊杂志排版;合理运用text-align:justify 有时会省去很多开发的时间,通过本文介绍text-align:justify实现文本两端对齐 兼容IE
    2015-08-08
  • javascript简单实现类似QQ头像弹出效果的方法

    javascript简单实现类似QQ头像弹出效果的方法

    这篇文章主要介绍了javascript简单实现类似QQ头像弹出效果的方法,可实现简单的页面元素弹出效果,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-08-08
  • js窗口关闭提示信息(兼容IE和firefox)

    js窗口关闭提示信息(兼容IE和firefox)

    这篇文章主要介绍了当关闭窗口的时候,显示窗口提示信息,友好的进行提示,防止丢失重要信息,需要的朋友可以参考一下
    2015-10-10
  • JavaScript实现动态增删表格的方法

    JavaScript实现动态增删表格的方法

    本篇文章主要介绍了JavaScript实现动态增删表格,可以实现表格增加和删除数据的功能,非常具有实用价值,有兴趣的可以了解一下
    2017-03-03
  • javascript实现划词标记+划词搜索功能

    javascript实现划词标记+划词搜索功能

    javascript实现划词标记+划词搜索功能...
    2007-03-03
  • javascript从作用域链谈闭包

    javascript从作用域链谈闭包

    这篇文章主要从作用域链谈闭包,闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现,本文针对闭包进行学习,需要的朋友可以参考下
    2015-12-12

最新评论