JS防抖和节流

对防抖和节流的一些理解,做一次记录。(之前项目中的需求是在输入框中输入内容之后,调接口返回值,然后不知道还有节流这波操作,然后就写了判断当鼠标失去焦点的时候调接口,后来大佬说可以使用节流来实现)

防抖和节流算起来应该属于性能优化的知识,但是处理不当或者是放任不管就容易引起浏览器卡死。就是在绑定scroll、resize这类事件时,当他发生时,被触发的频率非常高,间隔很近。如果事件中涉及到大量的位置计算、DOM操作、元素重绘等工作且这些工作无法在下一个scroll事件触发前完成,就会造成浏览器调帧。加之用户鼠标滚动往往时连续的,就会持续触发scroll事件导致调帧扩大、浏览器CPU使用率增加、用户体验受到影响。尤其时在涉及与后端的交互中,前端依赖于某中事件如resize、scroll,发送http请求,在这个过程中,如果不做防抖处理,那么在事件触发的一瞬间,就会有很多个请求发过去,增加了服务端的压力。

1.从滚动条监听的例子说起

先说一个常见的功能,很多网站会提供一个按钮:用于返回顶部。

这个按钮只会在滚动到距离顶部一定位置的时候才会出现,那么现在抽象出这个功能需求 --- 监听滚动条事件,返回当前滚条和顶部的距离。

这个需求很简单,直接写:

1 function showTop  () {
2     var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
3   console.log(‘滚动条位置:‘ + scrollTop);
4 }
5 window.onscroll  = showTop

但是:在运行的时候会发现:这个函数的默认执行频率太高了!以chrome为例,我们可以点击选中一个页面的滚动条,然后点击一次键盘的【向下方向键】,会发现函数执行了8-9次

然而实际上并不需要如此高频的反馈,毕竟浏览器的性能是有限的,不应该浪费在这里,所以需要优化这种场景。

2.防抖

基于上述的场景,首先提出第一种思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值:300ms

  • 如果在300ms内没有再次触发滚动事件,那么就执行函数。
  • 如果在300ms内再次触发滚动事件,那么当前的即使取消,重新开始计时。

效果就是:如果在短时间内大量触发同意事件,只会执行一次函数。

实现:既然前面都提到了计时,那实现的关键就在于setTimeOut这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现:

 1 /**
 2 * fn[function] 需要防抖的函数
 3 * delay[number] 毫秒,防抖期限值
 4 */
 5 function debounce(fn,delay){
 6   let timer = null;
 7   return function(){
 8     if(timer){
 9       //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
10       clearTimeout(timer)
11       timer = setTimeOut(fn,delay)
12     }else{
13       // 进入该分支说明当前并没有在计时,那么就开始一个计时
14       timer = setTimeOut(fn,delay)
15     }
16   }
17 }

当然 上述代码是为了贴合思路,方便理解。写完会发现其实timer = setTimeOut(fn,delay)是一定会执行的,所以可以稍微简化下:

 1 function debounce(fn,delay){
 2     let timer = null //借助闭包
 3     return function() {
 4         if(timer){
 5             clearTimeout(timer)
 6         }
 7         timer = setTimeout(fn,delay) // 简化写法
 8     }
 9 }
10 // 然后是旧代码
11 function showTop  () {
12     var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
13   console.log(‘滚动条位置:‘ + scrollTop);
14 }
15 window.onscroll = debounce(showTop,1000) // 为了方便观察效果我们取个大点的间断值,实际使用根据需要来配置

此时会发现,必须在停止滚动1s以后,才会打印出滚动条位置。

防抖也就实现了:定义即:

  • 对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次。

3.节流

继续思考,使用上面的防抖方案来处理问题的结果是:

  • 如果在限定时间段内,不断触发滚动事件(比如某个用户闲着无聊,按住滚动不断的拖来拖去),只要不停止触发,理论上就永远不会输出当前距离顶部的距离。

但是如果产品同学的期望处理方案是:即使用户不断拖动滚动条,也能在某个时间间隔之后给出反馈呢?

其实很简单:我们可以设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(类似于技能冷却时间)。

效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。

实现 这里借助setTimeout来做一个简单的实现,加上一个状态位valid来表示当前函数是否处于工作状态:

定时器方案

 1 function throttle(fn,delay){
 2   let valid = true;
 3   return function(){
 4        if(!valid){
 5          return false;
 6         }
 7        //执行函数并且在间隔期间内把状态位设为无效
 8            valid = false;
 9         setTimeout(()=>{
10           fn()
11           valid = true;
12         },delay)
13     }
14 }
15 /* 请注意,节流函数并不止上面这种实现方案,
16    例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。
17    也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样
18     */
19 // 以下照旧
20 function showTop  () {
21     var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
22   console.log(‘滚动条位置:‘ + scrollTop);
23 }
24 window.onscroll = throttle(showTop,1000) 

运行以上代码的结果:

如果一直拖着滚动条进行滚动,那么会以1s的时间间隔,持续输出当前位置和顶部的距离。

时间戳方案

 1 var throttle = function(fn,delay){
 2   var prev = Date.now();
 3   return function(){
 4     var context = this;
 5     var args = arguments;
 6     var now = Date.now();
 7     if(now -prev >=delay){
 8        fn.apply(context,args)
 9        prev = Date.now();
10        }
11   }
12 }
13 function handle(){
14   console.log(Math.random());
15 }
16 window.addEventListener(‘scroll‘,throttle(handle,1000));

时间戳+定时器

 1 var throttle = function(func, delay) {
 2      var timer = null;
 3      var startTime = Date.now();
 4      return function() {
 5              var curTime = Date.now();
 6              var remaining = delay - (curTime - startTime);
 7              var context = this;
 8              var args = arguments;
 9              clearTimeout(timer);
10               if (remaining <= 0) {
11                     func.apply(context, args);
12                     startTime = Date.now();
13               } else {
14                     timer = setTimeout(func, remaining);
15               }
16       }
17 }
18 function handle() {
19       console.log(Math.random());
20 }
21  window.addEventListener(‘scroll‘, throttle(handle, 1000));

4.其他应用场景举例

讲完了这两个技巧,下面介绍一下平时开发中常遇到的场景:

  1. 搜索框input事件,例如要支持输入实时搜索可以使用节流方案(间隔一段时间就必须查询相关内容),或者实现输入间隔大于某个值(如500ms),就当做用户输入完成,然后开始搜索,具体使用哪种方案要看业务需求。
  2. 页面resize事件,常见于需要做页面适配的时候。需要根据最终呈现的页面情况进行dom渲染(这种情形一般是使用防抖,因为只需要判断最后一次的变化情况)

5.总结

函数防抖:将几次操作合并为一个操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay内再次触发的话,就会取消之前的计时器而重新设置。这样一来。只有最后一次操作能被触发。

函数节流:使得一定时间内只触发一次函数,原理是通过判断是否到达一定时间来触发函数。

区别:函数节流不管事件触发多频繁,都会保证在规定的时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。比如在页面的无限加载场景下,需要用户在滚动页面时,每隔一段时间发一次ajax请求,而不是在啊用户停下滚动页面操作时才去请求数据。这种场景就适合用节流技术来实现。

原文地址:https://www.cnblogs.com/ichthyo-plu/p/11275965.html

时间: 2024-10-29 02:55:51

JS防抖和节流的相关文章

深入理解JS防抖与节流

参考博客:JS防抖和节流,感谢作者的用心分享 日常开发过程中,滚动事件做复杂计算频繁调用回调函数很可能会造成页面的卡顿,这时候我们更希望把多次计算合并成一次,只操作一个精确点,JS把这种方式称为debounce(防抖)和throttle(节流) 函数防抖 当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定时间到来之前,又触发了事件,就重新开始延时.也就是说当一个用户一直触发这个函数,且每次触发函数的间隔小于既定时间,那么防抖的情况下只会执行一次. function

2019 面试准备 - JS 防抖与节流 (超级 重要!!!!!)

Hello 小伙伴们,如果觉得本文还不错,记得给个 star , 你们的 star 是我学习的动力!GitHub 地址 本文涉及知识点: 防抖与节流 重绘与回流 浏览器解析 URL DNS 域名解析 TCP 三次握手与四次挥手 浏览器渲染页面 在本文中,jsliang 会讲解通过自我探索后关于上述知识点的个人理解,如有纰漏.疏忽或者误解,欢迎各位小伙伴留言指出. 如果小伙伴对文章存有疑问,想快速得到回复. 或者小伙伴对 jsliang 个人的前端文档库感兴趣,也想将自己的前端知识整理出来. 欢迎

js防抖和节流优化浏览器滚动条滚动到最下面时加载更多数据

防抖和节流,主要是用来防止过于平凡的执行某个操作,如浏览器窗口变化执行某个操作,监听某个input输入框keyup变化,瀑布流布局时Y轴滚动,图片加载. js函数的防抖 经过一段事件才执行某个操作,如果时间内又执行了该操作则延长时间重新开始计算 /* 不做处理input触发keyup事件 */ /* 输入快的话会出现输出结果多次重复 */ window.onload = function () { let obj = document.getElementById('input') obj.ad

JS防抖与节流

日常开发过程中,滚动事件做复杂计算频繁调用回调函数很可能会造成页面的卡顿,这时候我们更希望把多次计算合并成一次,只操作一个精确点,JS把这种方式称为debounce(防抖)和throttle(节流) 函数防抖 当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定时间到来之前,又触发了事件,就重新开始延时.也就是说当一个用户一直触发这个函数,且每次触发函数的间隔小于既定时间,那么防抖的情况下只会执行一次. function debounce(fn, wait) { va

详谈js防抖和节流

本文由小芭乐发表 0. 引入 首先举一个例子: 模拟在输入框输入后做ajax查询请求,没有加入防抖和节流的效果,这里附上完整可执行代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>没有防抖</title> <style type="text/css"></sty

js 防抖与节流原理

防抖和节流是前端应用开发中常见的两个功能,其原理都是利用闭包,缓存延迟时间.常用于窗口的resize.scroll,输入框内容校验等操作. (1)防抖,思路:在规定时间内未触发第二次,则执行,代码如下 function debounce(fn, delay) { let time = null ; //定时器 delay = delay || 500 //利用闭包,缓存delay return function() { let arg = arguments if(time) { clearTi

js函数的节流和防抖

js函数的节流和防抖 用户浏览页面时会不可避免的触发一些高频度触发事件(例如页面 scroll ,屏幕 resize,监听用户输入等),这些事件会频繁触发浏览器的重拍(reflow)和重绘(repaint)这会严重耗费浏览器性能,造成页面 卡顿. 举几个例子:比如说我们在滚动事件中要做一个复杂的计算,或者做一个按钮的防二次点击操作的需求,这些需求都会在频繁的事件 回调中做复杂计算,很有可能导致页面卡顿,这时候我们可以将多次计算合并为一次计算,只在一个精确点做操作.这些事可以利用 函数的防抖来实现

JS的防抖和节流

数个月之前,在一次前端的性能优化中,接触到了JS中防抖和节流,一开始还不明白他们的应用在哪里,可后来才知道,这是前端中最基础的性能优化,在绑定 scroll .resize 这类事件时,当它发生时,它被触发的频次非常高,间隔很近.如果事件中涉及到大量的位置计算.DOM 操作.元素重绘等工作且这些工作无法在下一个 scroll 事件触发前完成,就会造成浏览器掉帧.加之用户鼠标滚动往往是连续的,就会持续触发 scroll 事件导致掉帧扩大.浏览器 CPU 使用率增加.用户体验受到影响.尤其是在涉及与

前端进击的巨人(八):浅谈函数防抖与节流

本篇课题,或许早已是烂大街的解读文章.不过春招系列面试下来,不少伙伴们还是似懂非懂地栽倒在(-面试官-)深意的笑容之下,权当温故知新. JavaScript的执行过程,是基于栈来进行的.复杂的程序代码被封装到函数中,程序执行时,函数不断被推入执行栈中.所以 "执行栈" 也称 "函数执行栈". 函数中封装的代码块,一般都有相对复杂的逻辑处理(计算/判断),例如函数中可能会涉及到 DOM 的渲染更新,复杂的计算与验证, Ajax 数据请求等等. 前端页面的操作权,大部分