js中如何判断session的缓存数据是否清空_一文搞懂JS系列(五)之闭包应用-防抖,节流

写在最前面:这是我即将开始写的一个系列,主要是在框架横行的时代,虽然上班用的是框架,但是对于面试,以及技术进阶,JS基础知识的铺垫是锦上添花,也是不得不学习的一块知识,虽然开汽车的不需要很懂汽车,只需要掌握汽车的常用功能即可。但是如果你懂汽车,那你也能更好地开车,同理。当然,一篇文章也不会光光只讲一个知识点,一般会将有关联的知识点串联起来,一边记录自己的学习,一边分享自己的学习,互勉!如果可以的话,也 请给我点个赞,你的点赞也能让我更加努力地更新!

概览

  • 食用时间: 10-15分钟
  • 难度: 简单,别跑,看完再走
  • 食用价值: JS性能优化
  • 食材

先来看一段代码,这会是一个贯穿全文的案例,代码如下:

 <div id="content" style="height:150px;line-height:150px;
 text-align:center; color: #fff;background-color:black;
 font-size:80px;"></div>
 
 <script>
   let num = 1;
   const content = document.getElementById('content');
   function count() {
     content.innerHTML = num++;
   };
   content.onmousemove = count;
 </script>

可以看到,在黑色色块中移动的同时, addCount 函数被疯狂执行,接下来,我们来引入今天的主角,防抖节流

e831ff74f205b600384e9feff0509a2b.png

防抖

定义

将多次执行变为最后一次执行或执行,你可以理解为防止手抖

使用场景

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测
  • 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染

实现方式

  • 非立即执行版

这应该是最基础也是最常用的一个版本,先来看下代码

function debounce(func,wait) {
 let timeout;    //延时器变量
 return function () {
   const context = this;    //改变this指向
   const args = [...arguments];    //函数入参,另外使用了...扩展运算符
   if (timeout) clearTimeout(timeout);   //先判断有没有延时器,有则清空,毕竟要最后一次执行
   timeout = setTimeout(() => {      
     func.apply(context, args)     //apply调用传入方法
   }, wait);
 }
}

可以看到,方法 debounce() 有两个入参,一个方法名 func , 以及一个延时时间 wait ,单位 ms

接下来,使用 content.onmousemove = debounce(count,1000); 调用我们新写的非立即执行版的防抖,先来看下实际的运行效果,可以看到事件触发了以后,只有在触发以后的1s内不再触发,才会执行相应的方法,也就是 count++ 。如果停止时间间隔小于 wait 的值并且再次触发,那么将重新计算执行时间,计时器结束以后,再执行方法。总结而言就是触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间,也就是方法的执行是非立即执行

d5d30f79e7e65c043a5f58482882193b.png

整个方法的核心思想就是依靠变量 timeout ,用来控制当前是否存在定时器,如果有,则清空,清空玩以后再继续创建一个。所以,在多次执行的同时,不断清空再新建,直到停止执行以后,在停止执行以后的 wait 毫秒以后,延时器就会成功生效,方法就会被触发,也就是所谓的非立即执行,毕竟,还要等待延时器的延时 wait

  • 立即执行版

立即执行版就是在触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果,代码如下:

function debounce(func,wait,...args){
 let timeout;     //延时器变量
 return function(){
   const context = this;
   if (timeout) clearTimeout(timeout);
   let callNow = !timeout;    //是否立即执行
   timeout = setTimeout(() => {
     timeout = null;
   },wait)
   if(callNow) func.apply(context,args)
 }
}

可以看到的是, timeout 依然是延时器,主要核心控制是靠 callNow

① 在刚初始化的时候,没有定时器,所以刚开始 callNow=!timeout 执行完以后, callNowtrue ,再设置一个延时器,然后直接执行方法,这就是所谓的 立即执行
② 第二次的时候在进入的时候, if (timeout) 为真,将定时器进行清空, callNow=!timeout 为假,条件不成立
if(callNow) 不成立,函数不执行,因为 timeout = null ,往后将不再执行函数,直到延时器完成调用 timeout = null 之后再触发事件
④ 触发之后, timeout = nullcallNow 赋值为真, if(callNow)条件再次符合,完成执行函数

关于上面有一点, clearTimeout(timeout) 以后,console.log(timeout) 输出为 1

不相信的可以看一下下面的代码输出

let timer=setTimeout(()=>{

},1000);
clearTimeout(timer);
console.log(!timer);     //false

最后,让我们再来看一下实际使用效果,可以看到的是,触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果

1b2b2c441c28fe7fa946e834f014e499.png

节流

定义

将多次执行变为每隔一段时间执行一次

使用场景

  • 滚动加载,加载更多或滚到底部监听

实现方式

  • 时间戳版(立即执行版)

在持续触发事件的过程中,函数会立即执行,并且每隔一段时间执行一次,代码如下:

function throttle(func, wait, ...args){
   let pre = 0;
   return function(){
       const context = this;
       let now = Date.now();
       if (now - pre >= wait){
           func.apply(context, args);
           pre = Date.now();
       }
   }
}
① 首先定义了一个只有完成函数调用才更新当前时间的变量 pre ,然后定义了一个实时更新的当前时间 now
② 进入第一次计算时间间隔, now - pre >= wait 是必定成立的,所以函数会 立即触发
③ 触发完了以后,将 pre 的值进行更新,之后, now 的值会进行实时更新
④ 直到 now - pre >= wait 的条件成立,也就是现在的时间距离上次触发的时间大于等于 wait 的等待时间,函数会再次触发, (毕竟只要函数不触发,pre 的值不更新,而now一直在实时更新,时间长了,条件肯定会成立的)
⑤ 以此类推,完成了事件一直在触发,首次立即执行函数,之后函数只会隔一段时间执行

分析完了代码,让我们来看看实际运行效果,果然和我们的分析如出一辙:

d9770881462eafd8ad7a22b7fb706aa6.png
  • 延时器版(非立即执行版)

在持续触发事件的过程中,函数不会立即执行,并且每隔一段时间执行一次,在停止触发事件后,函数还会再执行一次,代码如下:

function throttle(func, wait, ...args){
    let timeout;
    return function(){
        const context = this;
        if(!timeout){
            timeout = setTimeout(() => {
                timeout = null;
                func.apply(context,args);
            },wait)
        }
    }
}
① 首先定义了一个延时器变量 timeout ,先判断是否有延时器,没有则创建,所以第一次进入函数的时候,会先创建一个延时器
② 再次进入函数的时候,因为当前已经存在延时器了,所以什么都不做
③ 什么都不做直到延时器的时间结束,函数开始执行, timeout 进行清空并且执行函数
④ 清空以后,再一次判断, if(!timeout) 条件成立,继续创建延时器
⑤ 以此类推, 有延时器就什么都不做,没有了延时器则创建
⑥ 即使不触发事件,延时器仍然存在,所以, 停止触发事件以后,函数仍然会再执行一次

分析完了代码,让我们来看看实际运行效果,果然和我们的分析如出一辙:

149e536b050aee2c96803334fbf86d0c.png

系列目录

  • 一文搞懂JS系列(一)之编译原理,作用域,作用域链,变量提升,暂时性死区
  • 一文搞懂JS系列(二)之JS内存生命周期,栈内存与堆内存,深浅拷贝
  • 一文搞懂JS系列(三)之垃圾回收机制,内存泄漏,闭包
  • 一文搞懂JS系列(四)之闭包应用-柯里化,偏函数
  • 一文搞懂JS系列(四)之闭包应用-防抖,节流
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值