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))

总结

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

(0)

相关推荐

  • JS防抖节流函数的实现与使用场景

    目录 一.什么是函数防抖 1.为什么需要函数防抖? 2.函数防抖的要点 3.函数防抖的实现 4.函数防抖的使用场景 二.什么是函数节流 1.函数节流的要点 2.函数节流的实现 3.函数节流的使用场景 总结 一.什么是函数防抖 概念:函数防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间. 1.为什么需要函数防抖? 前端开发过程中,有一些事件,常见的例如,onresize,scroll,mousemove ,

  • 浅谈JavaScript节流和防抖函数

    概念 节流函数 间隔固定的时间执行传入的方法 目的是防止函数执行的频率过快,影响性能.常见于跟滚动,鼠标移动事件绑定的功能. 防抖函数 对于接触过硬件的人也许更好理解,硬件按钮按下时,由于用户按住时间的长短不一,会多次触发电流的波动,加一个防抖函数就会只触发一次,防止了无意义的电流波动引起的问题. 按键防反跳(Debounce)为什么要去抖动呢?机械按键在按下时,并非按下就接触的很好,尤其是有簧片的机械开关,会在接触的瞬间反复的开合多次,直到开关状态完全改变. 应用在前端时,常见的场景是,输入框

  • 浅析JavaScript 函数防抖和节流

    函数防抖和节流都是对高频动作触发回调函数的一个优化,实现方式上有类似之处.先从使用场景做个区分. 防抖使用场景: 表单输入框校验 提交按钮避免重复提交 节流使用场景: scroll,mousemove,resize等 函数防抖(debounce) 表单输入框校验在用户不停的打字输入时并不需要向后台校验文本,只有当用户停下来一定时间后,这时候默认用户已经输入完毕了可以开始向后台提交文本了. 表单的提交按钮被用户多次连续点击时,显然并不需要每次点击都提交表单.仅在用户不点击之后,把最后一次的点击操作

  • 如何理解JS函数防抖和函数节流

    概述 函数防抖和函数节流都是定义一个函数,该函数接收一个函数作为参数,并返回一个添加了防抖或节流功能后的函数. 因此可以将函数防抖和函数节流看作是一个函数工厂,负责对传进来的函数进行相应的加工改造,然后产出一个新的带有某种功能的函数. 函数防抖是某一时间内只执行一次,而函数节流是间隔时间执行 假如有这样一个场景:在某一页面,有一个按钮是 "加载更多",这个按钮的作用就是使用 ajax 从后端服务器请求更多的数据展示在页面,我们都知道,ajax 请求的响应是一个异步的,会存在一定的响应时

  • web项目开发之JS函数防抖与节流示例代码

    目录 防抖 引入 防抖场景1(鼠标移入) 防抖场景2(键盘按键) 函数节流 防抖 经典应用常见: 手风琴效果 引入 没有做防抖的网站: 做了防抖的网站: 防抖场景1(鼠标移入) 抖动 : 用户本来不想触发这个交互,但是由于鼠标不小心抖动误触发交互事件. 例子: 想看第五张图片,.不想看2 3 4张. 但是鼠标从第1张滑到第五张时候,不小心放在了2 3 4上面.误触发. 函数防抖 : 用户连续多次触发某个事件,则只执行最后一次. 解决原理: 开启定时器,间隔时间内如果多次触发事件,则每一次都清除上

  • js节流防抖应用场景,以及在vue中节流防抖的具体实现操作

    故事背景: 项目有个需求是输入框在输入的时候进行搜索,展示下拉数据,但是没必要输入一个字都进行搜索,所以想到了在输入结束200毫秒后再进行搜索,从而引出来了 js的节流(throttle),防抖(debounce),在网上想找个现成的用下,但是好多都不对,于是就自己搞了. 先看看概念 函数防抖(debounce): 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时:典型的案例就是输入搜索:输入结束后n秒才进行搜索请求,n秒内又输入的内容,就重新计时. 函数节流(throttle)

  • JavaScript防抖与节流详解

    目录 防抖debounce 节流throttle 总结 防抖debounce 定义:对于短时间内连续触发的事件,比如滚动事件,防抖就是让某个时间期限内,事件处理函数只执行一次. 关于防抖,拿手指按压弹簧举例,用手指按压弹簧,一直按住,弹簧将不会弹起直到松开手指. 举例resize: function debounce(fn, wait){ var timer = null; return ()=>{ if(timer !== null){ clearTimeout(timer); } timer

  • JavaScript 防抖和节流详解

    目录 防抖 节流 总结 防抖 自动门感应到有人,打开门,并且开始5秒倒计时,在 5 s 内有另外一个人靠近到门,门感应到人,重新5秒倒计时 当事件被触发时,设定一个延迟,若期间事件又被触发,则重新设定延迟,直到延迟结束,执行动作 (防止多次触发) web 应用上面 改变页面大小的统计 滚动页面位置的统计 输入框连续输入的请求次数控制 一开始,点击按钮,console.log('pay money') <body> <button id="btn">click&l

  • JavaScript中函数的防抖与节流详解

    目录 一.函数的节流 1.1定义 1.2解决方法 1.3案例演示 1.3.1 代码演示 1.3.2 运行结果 1.3.3 添加函数节流操作 1.3.4 运行结果 二.函数的防抖 2.1 定义 2.2 解决方法 2.3 案例演示 2.3.1 代码展示 2.3.2 运行结果 2.3.3添加函数防抖操作 2.3.4 运行结果 总结 一.函数的节流 1.1 定义 同时触发多次函数执行,执行的是相同内容,要求只执行第一次请求. 例如scroll事件,鼠标滚动一次触发多次函数执行,只需要执行一次. 1.2

  • javascript防抖函数debounce详解

    定义及解读 防抖函数 debounce 指的是某个函数在某段时间内,无论触发了多少次回调,都只执行最后一次.假如我们设置了一个等待时间 3 秒的函数,在这 3 秒内如果遇到函数调用请求就重新计时 3 秒,直至新的 3 秒内没有函数调用请求,此时执行函数,不然就以此类推重新计时. 举一个小例子:假定在做公交车时,司机需等待最后一个人进入后再关门,每次新进一个人,司机就会把计时器清零并重新开始计时,重新等待 1 分钟再关门,如果后续 1 分钟内都没有乘客上车,司机会认为乘客都上来了,将关门发车. 此

  • JavaScript 函数节流详解及方法总结

    JavaScript 函数节流详解 浏览器一个网页的UI线程只有一个,他同时会处理界面的渲染和页面JavaScript代码的执行(简单扩展一下,浏览器或者JavaScript运行大环境并不是单线程,诸如ajax异步回调.hybrid框架内与native通信.事件队列.CSS运行线程等等都属于多线程环境,不过ES6引入了Promise类来减少了部分异步情况).因此当JavaScript代码运行计算量很大的方法时,就有可能阻塞UI线程,小则导致用户响应卡顿,严重的情况下浏览器会提示页面无响应是否强制

  • JS前端同源策略和跨域及防抖节流详解

    目录 引言 jQuery中JSONP的实现 防抖[重要] 缓存搜索的列表 1 定义全局缓存对象 2:将搜索结果存储到缓存对象中 3优先从缓存中获取搜索列表 节流[重点] 防抖和节流的区别 引言 协议,域名,端口相同,就具有相同的源 同源策略:浏览器提供的一个安全策略 跨域的出现原因:浏览器的同源策略不允许非同源的URL之间进行资源的交互 解决跨域由两种方式:JSONP, CORS JSONP: 只支持GET请求 通过script标签的src属性,请求跨域的数据接口,并通过函数调用的形式,接收跨域

  • 基于JavaScript表单脚本(详解)

    什么是表单? 一个表单有三个基本组成部分: 表单标签:这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务器的方法. 表单域:包含了文本框.密码框.隐藏域.多行文本框.复选框.单选框.下拉选择框和文件上传框等. 表单按钮:包括提交按钮.复位按钮和一般按钮:用于将数据传送到服务器上的CGI脚本或者取消输入,还可以用表单按钮来控制其他定义了处理脚本的处理工作. JavaScript与表单间的关系:JS最初的应用就是用于分担服务器处理表单的责任,打破依赖服务器的局面,尽管目前web和jav

  • JavaScript数据结构链表知识详解

    最近在看<javascript数据结构和算法>这本书,补一下数据结构和算法部分的知识,觉得自己这块是短板. 链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中不是连续放置的.每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成. 好处:可以添加或移除任意项,它会按需扩容,且不需要移动其他元素. 与数组的区别: 数组:可以直接访问任何位置的任何元素: 链表:想要访问链表中的一个元素,需要从起点(表头)开始迭代列表直到找到所需的元素. 做点小笔记. funct

  • JavaScript里 ==与===区别详解

    1.对于string,number等基础类型,==和===是有区别的 1)不同类型间比较,==之比较"转化成同一类型后的值"看"值"是否相等,===如果类型不同,其结果就是不等 2)同类型比较,直接进行"值"比较,两者结果一样 2.对于Array,Object等高级类型,==和===是没有区别的 进行"指针地址"比较 3.基础类型与高级类型,==和===是有区别的 1)对于==,将高级转化为基础类型,进行"值&quo

  • JavaScript String 对象常用方法详解

    字符串是不可变的.字符串方法,不会改变原有字符串的内容,而是返回一个新字符串. charAt():返回在指定位置的字符. concat():连接两个字符串文本,并返回一个新的字符串. indexOf(): indexOf() 方法返回指定值在字符串对象中首次出现的位置. match(): 使用正则表达式与字符串相比较. replace(): 被用来在正则表达式和字符串直接比较,然后用新的子串来替换被匹配的子串. slice(): 摘取一个字符串区域,返回一个新的字符串. split(): 通过分

随机推荐