1. 程式人生 > 其它 >js中如何判斷session的快取資料是否清空_一文搞懂JS系列(五)之閉包應用-防抖,節流...

js中如何判斷session的快取資料是否清空_一文搞懂JS系列(五)之閉包應用-防抖,節流...

技術標籤:js中如何判斷session的快取資料是否清空js判斷定時器是否啟動js判斷時間是否大於當前時間js定時器一直開啟會增加記憶體麼js延時函式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系列(四)之閉包應用-防抖,節流