什么是節流函數?
簡單講就是讓一個函數無法在短時間內連續調用,只有當上一次函數執行后過了規定的時間間隔,才能進行下一次該函數的調用。或者說你在操作的時候不會馬上執行該函數,而是等你不操作的時候才會執行。
函數節流的原理:
可通過使用定時器,在操作的時候讓函數延時執行,如果在這個時間內還在操作,則清除原來的定時器,再創建一個新的定時器執行。
函數節流非常適用於函數被頻繁調用的場景,例如:window.onresize() 事件、mousemove 事件、上傳進度等情況。使用 throttle API 很簡單,那應該如何實現 throttle 這個函數呢?
實現的方案有以下兩種:
- 第一種是用時間戳來判斷是否已到執行時間,記錄上次執行的時間戳,然后每次觸發事件執行回調,回調中判斷當前時間戳距離上次執行時間戳的間隔是否已經達到時間差(Xms) ,如果是則執行,並更新上次執行的時間戳,如此循環。
- 第二種方法是使用定時器,比如當 scroll 事件剛觸發時,打印一個 hello world,然后設置個 1000ms 的定時器,此后每次觸發 scroll 事件觸發回調,如果已經存在定時器,則回調不執行方法,直到定時器觸發,handler 被清除,然后重新設置定時器。
這里我們采用第一種方案來實現,通過閉包保存一個 previous 變量,每次觸發 throttle 函數時判斷當前時間和 previous 的時間差,如果這段時間差小於等待時間,那就忽略本次事件觸發。如果大於等待時間就把 previous 設置為當前時間並執行函數 fn。
我們來一步步實現,首先實現用閉包保存 previous 變量。
const throttle = (fn, wait) => { // 上一次執行該函數的時間 let previous = 0 return function(...args) { console.log(previous) ... } }
執行throttle函數后會返回一個新的function,我們命名為betterFn。
const betterFn = function(...args) { console.log(previous) ... }
betterFn 函數中可以獲取到 previous 變量值也可以修改,在回調監聽或事件觸發時就會執行 betterFn,即 betterFn(),所以在這個新函數內判斷當前時間和 previous 的時間差即可。
const betterFn = function(...args) { let now = +new Date(); if (now - previous > wait) { previous = now // 執行 fn 函數 fn.apply(this, args) } }
結合上面兩段代碼就實現了節流函數,所以完整的實現如下。
// fn 是需要執行的函數 // wait 是時間間隔 const throttle = (fn, wait = 50) => { // 上一次執行 fn 的時間 let previous = 0 // 將 throttle 處理結果當作函數返回 return function(...args) { // console.log(previous) // 獲取當前時間,轉換成時間戳,單位毫秒 let now = +new Date() // 將當前時間和上一次執行函數的時間進行對比 // 大於等待時間就把 previous 設置為當前時間並執行函數 fn if (now - previous > wait) { previous = now //通過apply還原this指向事件 fn.apply(this, args) } } // DEMO // 執行 throttle 函數返回新函數 const betterFn = throttle(() => console.log('fn 函數執行了'), 1000) console.log(betterFn) // 每 10 毫秒執行一次 betterFn 函數,但是只有時間差大於 1000 時才會執行 fn setInterval(betterFn, 1000) }
轉自:https://www.cnblogs.com/zhangycun/p/10949450.html
<body> <button class="demo">按鈕</button> <script> function debounce(func, delay) { let timer; // 通過閉包使timer一直存在內存中 return function (...args) { //清除1秒之內重復點擊的事件 if (timer) { console.log(timer) clearTimeout(timer); } //重新生成定時器 /*setTimeout() 方法的返回值是一個唯一的數值,這個數值有什么用呢?如果你想要終止 setTimeout() 方法的執行,那就必須使用 clearTimeout() 方法來終止,而使用這個方法的時候,系統必須知道你到底要終止的是哪一個 setTimeout() 方法 (因為你可能同時調用了好幾個 setTimeout() 方法),這樣 clearTimeout() 方法就需要一個參數,這個參數就是 setTimeout() 方法的返回值 (數值),用這個數值來唯一確定結束哪一個 setTimeout() 方法。 */ timer = setTimeout(() => { func.apply(this, args); //通過apply還原this指向事件 }, delay) console.log(timer) } } // 點擊元素1秒后執行,如果1秒內重復點擊會清空之前定時,重新生成定時器! document.querySelector('.demo').addEventListener('click',debounce((e)=>{ // 需要執行的代碼 console.log(e); },1000),false); </script> </body>
