摘要:最簡單的案例以最簡單的情景為例在某一時刻點只調用一次函數,那么將在時間后才會真正觸發函數。后續我們會逐漸增加黑色鬧鐘出現的復雜度,不斷去分析紅色鬧鐘的位置。
序
相比網上教程中的 debounce 函數,lodash 中的 debounce 功能更為強大,相應的理解起來更為復雜;
解讀源碼一般都是直接拿官方源碼來解讀,不過這次我們采用另外的方式:從最簡單的場景開始寫代碼,然后慢慢往源碼上來靠攏,循序漸進來實現 lodash 中的 debounce 函數,從而更深刻理解官方 debounce 源碼的用意。
為了減少純代碼帶來的晦澀感,本文以圖例來輔助講解,一方面這樣能減少源碼閱讀帶來的枯燥感,同時也讓后續回憶源碼內容更加的具體形象。(記住圖的內容,后續再寫出源碼也變得簡單些)
在本文的末尾還會附上簡易的 debounce & throttle 的實現的代碼片段,方便平時快速用在簡單場景中,免去引用 lodash 庫。
本文屬于源碼解讀類型的文章,對 debounce 還不熟悉的讀者建議先通過參考文章(在文末)了解該函數的概念和用法。1、用圖例解析 debounce 源碼
附源碼 debounce: https://github.com/boycgit/ts...
首先搬出 debounce(防抖)函數的概念:函數在 wait 秒內只執行一次,若這 wait 秒內,函數高頻觸發,則會重新計算時間。
看似簡單一句話,內含乾坤。為方便行文敘述,約定如下術語:
假定我們要對 func 函數進行 debounce 處理,經 debounced 后的返回值我們稱之為 debounced func
wait 表示傳入防抖函數的時間
time 表示當前時間戳
lastCallTime 表示上一次調用 debounced func 函數的時間
lastInvokeTime 表示上一次 func 函數執行的時間
result 是每次調用 debounced func 函數的返回值
用 time 表示當前時間
本文將搭配圖例 + 程序代碼的方式,將上述概念具象到圖中。
2、最簡單的案例以最簡單的情景為例:在某一時刻點只調用一次 debounced func 函數,那么將在 wait 時間后才會真正觸發 func 函數。
將這個情景形成一幅圖例,最終繪制出的圖如下所示:
下面我們詳細講解這幅圖的產生過程,其實不難,基本上看一遍就懂。
首先繪制在圖中放置一個黑色鬧鐘表示用戶調用 debounced func 函數:(同時用 lastCallTime 標示出最近一次調用 debounced func 的時間)
同時在距離該黑色鬧鐘 wait 處放置一個藍色鬧鐘,表示setTimout(..., wait),該藍色鬧鐘表示未來當代碼運行到該時間點時,需要做一些判斷:
為了標示出表示程序當前運行的進度(當前時間戳),我們用橙紅色滑塊來表示:
當紅色滑塊到達該藍色鬧鐘處的時候,藍色鬧鐘會進行判斷:因為當前滑塊距離最近的黑色鬧鐘的時間差為 wait:
故而做出判斷(依據 debounce 函數的功能定義):需要觸發一次 func 函數,我們用紅色鬧鐘來表示 func 函數的調用,所以就放置一個紅色鬧鐘
很顯然藍色和紅色鬧鐘重疊起來的。
同時我們給紅色鬧鐘標上 lastInvokeTime,記錄最近一次調用 func 的時間:
注意 lastInvokeTime 和 lastCallTime 的區別,兩者含義是不一樣的
這樣我們就完成了最簡單場景下 debounce 圖例的繪制,簡單易懂。
后續我們會逐漸增加黑色鬧鐘出現的復雜度,不斷去分析紅色鬧鐘的位置。這樣就能將理解 debounce 源碼的問題轉換成“根據圖上黑色鬧鐘的位置,請畫出紅色鬧鐘位置”的問題,而分析紅色鬧鐘位置的過程中也就是理解 debounce 源碼的過程;
用圖例方式輔助理解源碼的方式可以減少源碼閱讀帶來的枯燥感,同時后續回憶源碼內容起來也更加具體形象。
為避免后續寫文章到處解釋圖中元素的概念含義,這里不妨先羅列出來,如果閱讀過程中忘記到這里回憶一下也會方便許多:
橫線代表時間軸,橙紅色滑塊代表當前時間 time
每個黑色箭頭表示 debounced func 函數的調用
黑色鬧鐘表示調用 debounced func 函數時的時間,最后一次黑色鬧鐘上標上 lastCallTime,表示最近一次調用的時間戳;
紅色鬧鐘表示調用 func 函數的時間,最后一次紅色鬧鐘上標上 lastInvokeTime,表示最近一次調用的時間戳;
此外還有一個藍色鬧鐘,表示 setTimeout 時間戳(用來規劃 func 函數執行的時間),每次時間軸上的橙紅色滑塊到這個時間點就要做判斷:是執行 func 或者推遲藍色鬧鐘位置
有關藍色鬧鐘,這里有兩個注意點:
時間軸上最多同時只有一個藍色鬧鐘;
只有在第一次調用 debounced func 函數時才會在 wait 時間后放置藍色鬧鐘,后續鬧鐘的出現位置就由藍色鬧鐘自己決策(下文會舉例說明)
3、有 N 多個黑色鬧鐘的場景現在我們來一個稍微復雜的場景:
假如在 wait 時間內(記住這個前提條件)調用 n 次 debounced func 函數,如下所示:
第一次調用 debounced func 函數會在 wait 時間后放置藍色鬧鐘(只有第一次調用會放置藍色鬧鐘,后續鬧鐘的位置由藍色鬧鐘自己決策):
以上就是描述,那么問題來了:請問紅色鬧鐘應該出現在時間軸哪個位置?
3.1、分析紅色鬧鐘出現的位置我們只關注最后一個黑色鬧鐘,并假設藍色鬧鐘距離該黑色鬧鐘時間間隔為 x:
那么第一個黑色鬧鐘和最后一個黑色鬧鐘的時間間隔是 wait - x:
接下來我們關注橙紅色滑塊(即當前時間time)到達藍色鬧鐘的時,藍色鬧鐘開始做決策:計算可知 x < wait,此時藍色鬧鐘決定不放置紅色鬧鐘(即不觸發 func),而是將藍色鬧鐘往后挪了挪,挪動距離為 wait - x,調整完之后的藍色鬧鐘位置如下:
之所以挪 wait - x 的距離,是因為挪完后的藍色鬧鐘距離最后一個黑色鬧鐘恰好為 wait 間隔(從而保證 debounce 函數至少間隔 wait 時間 才觸發的條件):
從挪移之后開始,到下一次橙色鬧鐘再次遇到藍色鬧鐘這段期間,我們暫且稱之為 ”藍色決策間隔期“(請忍耐這抽象的名稱,畢竟我想了好久),藍色鬧鐘基于此間隔期的內容來進行決策,只有兩種決策:
如果在”藍色決策間隔期“內沒有黑鬧鐘出現,那么紅色滑塊達到藍色鬧鐘的時候,藍色鬧鐘計算獲知當前藍色鬧鐘距離上一個黑色鬧鐘的時間間隔不少于 wait(time - lastCallTime >= wait),那就會放置紅色鬧鐘(即調用 func),目標達成;
如果在”藍色決策間隔期“內仍舊有黑鬧鐘出現,那么當橙紅色滑塊到達藍色鬧鐘時,藍色鬧鐘又會重新計算與該間隔期內最后一只黑色鬧鐘的距離 y,隨后 又會往后挪動位置 wait-y,再一次保證藍色鬧鐘距離最后一個黑色鬧鐘恰好為 wait 間隔 —— 沒錯,又形成了新的 ”藍色決策間隔期“;那接下去的分析就又回到了 這里兩點(即遞歸決策),直到能放置到紅鬧鐘為止。
從上我們可以看到,藍色鬧鐘一直保持 ”紳士“ 風范,隨著黑色鬧鐘的逼近,藍色鬧鐘一直保持”克制“態度,不斷調整自己的位置,讓調整后的位置總是和最后一個黑色鬧鐘保持 wait 的距離。
3.2、用代碼描述圖例過程我們用代碼將上述的過程描述出來,就是下面這個樣子:
function debounce(func, wait, options) { var lastArgs, lastThis, result, timerId, lastCallTime, lastInvokeTime = 0, trailing = true; wait = toNumber(wait) || 0; // 紅色滑塊達到藍色鬧鐘時,藍色鬧鐘根據條件作出決策 function timerExpired() { var time = now(); // 決策 1: 滿足放置紅色鬧鐘的條件,則放置紅鬧鐘 if (shouldInvoke(time)) { return trailingEdge(time); } // 否則,決策 2:將藍色鬧鐘再往后挪 `wait-x` 位置,形成 ”藍色決策間隔期“ timerId = setTimeout(timerExpired, remainingWait(time)); } // === 以下是具體決策中的函數實現 ==== // 做出 ”應當放置紅色鬧鐘“ 的決策的條件:藍色鬧鐘和最后一個黑色鬧鐘的間隔不小于 wait 間隔 function shouldInvoke(time) { var timeSinceLastCall = time - lastCallTime; return ( timeSinceLastCall >= wait ); } // 具體函數:放置紅色鬧鐘 function trailingEdge(time) { timerId = undefined; if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = lastThis = undefined; return result; } // 具體函數 - 子函數:在時間軸上放置紅鬧鐘 function invokeFunc(time) { var args = lastArgs, thisArg = lastThis; lastArgs = lastThis = undefined; lastInvokeTime = time; result = func.apply(thisArg, args); return result; } // 具體函數:計算讓藍色鬧鐘往后挪 wait-x 位置 function remainingWait(time) { var timeSinceLastCall = time - lastCallTime, timeWaiting = wait - timeSinceLastCall; return timeWaiting ; } // ============== // 主流程:讓紅色滑塊在時間軸上前進(即 debounced func 函數的執行) function debounced() { var time = now(); lastArgs = arguments; lastThis = this; lastCallTime = time; if (timerId === undefined) { timerId = setTimeout(timerExpired, wait); } return result; } return debounced; }
這部分代碼還請不要略過,因為代碼是從debounce源碼中整理過來,除了函數順序略有調整外,源碼風格保持原有的,相當于直接閱讀源碼。每個函數都有注釋,對比著圖例閱讀下來相信讀完會有收獲的。
上述這份代碼已經包含了 debounce 源碼的核心骨架,接下來我們繼續擴展場景,將源碼內容豐滿起來。
4、豐富功能特性 4.1、支持 leading 特性leading 功能簡單理解就是,在第一次(注意這個條件)放下黑色鬧鐘的時候:
立即放置紅鬧鐘,同時在
此后 wait 處放置方式藍色鬧鐘(注:第一次放下黑色鬧鐘的時候,按理說也會在 wait 處放下藍色鬧鐘,考慮既然 leading 也有這種操作,那么就不多此一舉。記?。赫麄€時間軸上最多只能同時有一個藍色鬧鐘)
用圖說話:
第一次放置黑色鬧鐘的時候,會疊加上紅色鬧鐘(當然這個紅色鬧鐘上會標示 lastInvokeTime),另外在 wait 間隔后會有藍色鬧鐘。其他流程和之前案例分析一樣。
在代碼層面,我們給剛才的 debounce 函數添加 leading 功能(通過 options.leading 開啟)、新增一個 leadingEdge 方法后,再微調剛才的代碼:
function debounce(func, wait, options) { ... var leading = false; // 默認不開啟 leading = !!options.leading; // 通過 options.leading 開啟 ... // 首先:新增執行 leading 處的操作的函數 function leadingEdge(time) { lastInvokeTime = time; // 設置 lastInvokeTime 時間標簽 timerId = setTimeout(timerExpired, wait); // 同時在此后 `wait` 處放置一個藍色鬧鐘 return leading ? invokeFunc(time) : result; // 如果開啟,直接放置紅色鬧鐘;否則直接返回 result 數值 } ... // 其次:給放置紅色鬧鐘新增一種條件 function shouldInvoke(time) { ... return ( lastCallTime === undefined || // 初次執行時 timeSinceLastCall >= wait // 或者前面分析的條件,兩次 `debounced func` 調用間隔大于 wait ); } // 注意:放置完紅色鬧鐘后,記得要清空 timerId,相當于清空時間軸上藍色鬧鐘; function trailingEdge(time) { timerId = undefined; ... } // 最后:leading 邊界調用 function debounced(){ ... var isInvoking = shouldInvoke(time); // 判斷是否可以放置紅色鬧鐘 ... if (isInvoking) { // 如果可以放置紅色鬧鐘 if (timerId === undefined) { // 且當時間軸上沒有藍色鬧鐘 // 執行 leading 邊界處操作(放置紅色鬧鐘 或 直接返 result) return leadingEdge(lastCallTime); } } ... return result; } return debounced; }4.2、支持 maxWait 特性
要理解這個 maxWait 特性,我們先看一種特殊情況,在 {leading: false} 下, 時間軸上我們很密集地放置黑色鬧鐘:
按之前的所述規則,我們的藍色鬧鐘一直保持紳士態度,隨著黑色鬧鐘的逼近,藍色鬧鐘將不斷將調整自己的位置,讓自己調整后的位置總是和最后一個黑色鬧鐘保持 wait 的距離:
那么在這種情況下,如果黑色鬧鐘一直保持這種密集放置狀態,理論上就紅色鬧鐘就沒有機會出現在時間軸上。
那在這種情況下能否實現一個功能,無論黑色鬧鐘多么密集,時間軸上最多隔 maxWait 時間就出現紅色鬧鐘,就像下圖那樣:
有了這個功能屬性后,藍色鬧鐘從此 ”變得堅強“,也有了 "底線",縱使黑色鬧鐘的不斷逼近,也會堅守 maxWait 底線,到點就放置紅色鬧鐘。
實現該特性的大致思路如下:
maxWait 是與 lastInvokeTime 共同協作
在藍色鬧鐘計算后退距離時,maxWait 發揮作用;在沒有 maxWait 的時候,是按上一次黑色鬧鐘進行測距,保證調整后的藍色鬧鐘和黑色鬧鐘保持 wait 的距離;而在有了 maxWait 后,藍色鬧鐘調整距離還會考慮上一次紅色鬧鐘的位置,保持調整后鬧鐘的位置和紅色鬧鐘距離不能超過 maxWait,這就是底線了,到了一定程度,就算黑色鬧鐘在逼近,藍色鬧鐘也不會 ”退縮“:
從代碼層面上看, maxWait 具體是在 remainingWait 方法 和 shouldInvoke 中發揮作用的:
function debounce(func, wait, options) { ... var lastInvokeTime = 0; // 初始化 var maxing = false; // 默認沒有底線 maxing = "maxWait" in options; maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; // 從 options.maxWait 中獲取底線數值 ... // 首先,在在藍色鬧鐘決策后退多少距離時,maxWait 發揮了作用 function remainingWait(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, timeWaiting = wait - timeSinceLastCall; // 在這里發揮作用,保持底線 return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; } ... // 其次:針對 `maxWait`,給放置紅色鬧鐘新增一種可能條件 function shouldInvoke(time) { ... var timeSinceLastInvoke = time - lastInvokeTime; // 獲取距離上一次紅色鬧鐘的時間間隔 return ( lastCallTime === undefined || // 初次執行時 timeSinceLastCall >= wait || // 或者前面分析的條件,兩次 `debounced func` 調用間隔大于 wait (maxing && timeSinceLastInvoke >= maxWait) // 兩次紅色鬧鐘間隔超過 maxWait ); } // 最后:leading 邊界調用 function debounced(){ ... var isInvoking = shouldInvoke(time); // 判斷是否可以放置紅色鬧鐘的條件 ... if (isInvoking) { // 如果可以放置紅色鬧鐘 ... // 邊界情況的處理,保證在緊 loop 中能正常保持觸發 if (maxing) { timerId = setTimeout(timerExpired, wait); return invokeFunc(lastCallTime); } } ... return result; } return debounced; }
因此,maxWait 能夠讓紅色鬧鐘保證在 maxWait 間隔內至少出現 1 次;
4.3、支持 cancel / flush 方法這兩個函數是為了能隨時控制 debounce 的緩存狀態;
其中 cancel 方法源碼如下:
// 取消防抖 function cancel() { if (timerId !== undefined) { clearTimeout(timerId); } lastInvokeTime = 0; lastArgs = lastCallTime = lastThis = timerId = undefined; }
調用該方法,相當于直接在時間軸上去除藍色鬧鐘,這樣紅色方塊(時間)就永遠遇見不了藍色鬧鐘,那樣也就不會有放置紅色鬧鐘的可能了。
其中 flush 方法源碼如下:
function flush() { return timerId === undefined ? result : trailingEdge(now()); }
非常直觀,調用該方法相當于直接在時間軸上放置紅色鬧鐘。
至此,我們已經完整實現了 lodash 的 debounce 函數,也就相當于閱讀了一遍其源碼。
5、實現 throttle 函數在完成上面 debounce 功能和特性后(尤其是 maxWait 特性),就能借助 debounce 實現 throttle 函數了。
看 throttle 源碼 就能明白:
function throttle(func, wait, options) { var leading = true, trailing = true; // ... return debounce(func, wait, { "leading": leading, "maxWait": wait, "trailing": trailing }); }
所以在 lodash 中,只需要 debounce 函數即可,throttle 相當于 ”充話費“ 送的。
至此,我們已經解讀完 lodash 中的 debounce & throttle 函數源碼;
最后附帶一張 lodash 實現執行效果圖,用來自測是否真的理解通透:
注:此圖取自于文章《 聊聊lodash的debounce實現》6、小結
在前端領域的性能優化手段中,防抖(debounce)和節流(throttle)是必備的技能,網上隨便一搜就有很多文章去分析解釋,不乏優秀的文章使用 圖文混排 + 類比方式 深入淺出探討這兩函數的用法和使用場景(見文末的參考文檔)。
那我為什么還要寫這一篇文章?
緣起前兩天手動將 lodash 中的 debounce 和 throttle 兩個函數 TS 化的需求,而平時我也只是使用并沒有在意它們真正的實現原理,因此在遷移過程我順帶閱讀了一番 lodash 中這兩個函數的源碼。
具體原因和遷移過程請移步《技巧 - 快速 TypeScript 化 lodash 中的 throttle & debounce 函數》
本文嘗試提供了另一個視角去解讀,通過時間軸 + 鬧鐘圖例 + 代碼的方式來解讀 lodash 中的 debounce & throttle 源碼;
整個流程下來只要理解了黑色、藍色、紅色這 3 種鬧鐘的關系,那么憑著理解力去實現簡版 lodash 的 debounce 函數并非難事。
當然上述的敘述中,略過了很多細節和存在性的判斷(諸如 timeId 的存在性判斷、isInvoking的出現位置等),省略這主要是為了降低源碼閱讀的難度;(實際中這些細節的處理有時候反而很重要,是代碼健壯性不可或缺的一部分)
希望本文能對讀者理解 lodash 中的 debounce & throttle 源碼有些許的幫助,歡迎隨時關注微信公眾號或者技術博客留言交流。
【附】代碼片段如果在你僅僅需要應付簡單的一些場景,也可以直接使用下方的代碼片段。
A. 簡易 debounce - 只實現 trailing 情況防抖函數的概念:函數在 n 秒內只執行一次,若這 n 秒內,函數高頻觸發,則會重新計算時間。
將這段話翻譯成代碼,你會發現并不難:
//防抖代碼最簡單的實現 function debounce(func, wait) { let timerId, result; return function() { if(timerId){ clearTimeout(timerId); // 每次觸發 都清除當前timer,重新設置時間 } timerId = setTimeout(function(){ result = func.apply(this, arguments); }, wait); return result; } }
debounce 返回閉包(匿名函數)
假如調用該閉包兩次:
如果調用兩次間隔 < wait 數值,先前調用會被 clearTimeout ,也就不執行;最終只執行 1 次調用(即第 2 次的調用)
如果調用兩次間隔 > wait 數值,當執行 clearTimeout 的時候,前一次調用已經執行了;所以最終這兩次調用都會執行
上述的實現,是最經典的 trailing 情況,即以 wait 間隔結束點作為函數調用計時點,是我們平時用的最多的場景
B. 簡易 debounce - 只實現 leading 功能另外用得比較多的就是以 wait 間隔開始點作為函數調用計時點,即 leading 功能。
將上面代碼中最后的 setTimeout 內容改成 timerId = undefined ,而將 fn.apply 提取出來加個 if 條件語句就行 ,修改后代碼如下:
//防抖代碼最簡單的實現 function debounce(func, wait) { let timerId, result; return function() { if(timerId){ clearTimeout(timerId); // 每次觸發 都清除當前timer,重新設置時間 } if(!timerId){ result = fn.apply(this, arguments); } timerId = setTimeout(function() { timerId = undefined; }, wait); return result; } }
fn.apply(lastThis, lastArgs) 之所以用 if 條件包裹,是針對首次調用的邊界情況
debounce 仍舊返回閉包(匿名函數)
timerId 是閉包變量,相當于標志位,通過它可以知道某個函數的調用是否在上一次函數調用的影響范圍內
假如調用該閉包兩次:
如果調用兩次間隔 < wait 數值,后調用因為仍在前一次的 wait 影響范圍內,所以會被 clearTimeout 掉;最終只執行 1 次調用(即第 1 次的調用)
如果調用兩次間隔 > wait 數值,當執行第二次時 timerId 已經是 underfined 的,所以會立即執行 函數,所以最終這兩次調用都會執行
C. 簡易 throttle 函數throttle 函數的概念:函數在 n 秒內只執行一次,若這 n 秒內還在有函數調用的請求都直接被忽略掉。
實現原理也很簡單:定義開關變量 canRun,在定時開啟的這段時間內控制這個開關變量為canRun = false(上鎖),執行完后才讓 canRun = true 即可。
function throttle(func, wait) { let canRun = true return function () { if (!canRun) { return // 如果開關關閉了,那就直接不執行下邊的代碼 } canRun = false // 持續觸發的話,run一直是false,就會停在上邊的判斷那里 setTimeout(() => { func.apply(this, arguments) canRun = true // 定時器到時間之后,會把開關打開,我們的函數就會被執行 }, wait) } }參考文章
Debouncing and Throttling Explained Through Examples:首推這篇經典的文章,本文詳細描述了 lodash 中的 debounce 和 throttle 的思路設計;里面使用 圖文混排 深入淺出探討這兩函數的用法和具體使用場景,更為難得還嵌入有可交互 demo,能即刻感受這兩方法的具體使用方式;嫌看英文麻煩的可以看中文版 《實例解析防抖動(Debouncing)和節流閥(Throttling)》
防抖(debounce)函數的作用是什么?有哪些應用場景,請實現一個防抖函數:討論帖子,里面有不少的相關信息和資源
淺談 Underscore.js 中 _.throttle 和 _.debounce 的差異:很不錯的釋義文章,電梯類比秒懂
lodash.debounce: lodash debounce 多帶帶的庫,附官方文檔
防抖(debounce)函數的作用是什么:解釋了 debounce 函數的原理和實現
聊聊lodash的debounce實現:作者對比了自己的實現和 lodash 中的實現
Confused about the maxWait option for LoDash’s debounce method:解釋 ‘maxWait’ 的作用
第 3 題:什么是防抖和節流?有什么區別?如何實現:面試題,簡單快速實現防抖和節流這兩個函數
函數的防抖和節流是個啥???:用通俗的例子講解這兩個概念和實現
從lodash源碼學習節流與防抖:詳細注釋 lodash 中的 debounce 函數的實現
下面的是我的公眾號二維碼圖片,歡迎關注交流。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105977.html
摘要:背景需要包寫起來爽,然而如果遇到沒有現成的化的工具函數,就需要自己想辦法弄出一份類型聲明文件了。最為重要的是,這種遷移方面我們可以隨意自定義化中所需要的工具函數,遷移粒度都可以由自己控制。 1、背景 1.1、需要 TS 包 TypeScript 寫起來爽,然而如果遇到沒有現成的 TS 化的工具函數,就需要自己想辦法弄出一份類型聲明文件了。 前兩天要寫的小工具庫(Typescript 語...
摘要:如果我們的回調函數較為復雜,頁面的性能就會變差。而可以保證穩定的時間間隔執行一次回調函數。但需要弄清楚的是,無論是還是,控制的都是回調函數的執行,而不是事件的監聽。 前言 假設現在有個需求:監聽滑動事件,并執行回調。當你用觸摸板或者鼠標滑動頁面時,每秒鐘大概會觸發幾十次scroll事件,而當你在手機等移動終端上滑動頁面時,每秒就會觸發一百次scroll事件。如果我們的回調函數較為復雜,...
摘要:事情是如何發生的最近干了件事情,發現了源碼的一個。樓主找到的關于和區別的資料如下關于拿來主義為什么這么多文章里會出現澤卡斯的錯誤代碼樓主想到了一個詞,叫做拿來主義。的文章,就深刻抨擊了拿來主義這一現象。 事情是如何發生的 最近干了件事情,發現了 underscore 源碼的一個 bug。這件事本身并沒有什么可說的,但是過程值得我們深思,記錄如下,各位看官仁者見仁智者見智。 平時有瀏覽別...
摘要:舉例舉例通過拖拽瀏覽器窗口,可以觸發很多次事件。不支持,所以不能在服務端用于文件系統事件。總結將一系列迅速觸發的事件例如敲擊鍵盤合并成一個單獨的事件。確保一個持續的操作流以每毫秒執行一次的速度執行。 Debounce 和 Throttle 是兩個很相似但是又不同的技術,都可以控制一個函數在一段時間內執行的次數。 當我們在操作 DOM 事件的時候,為函數添加 debounce 或者 th...
閱讀 1834·2021-09-14 18:03
閱讀 2271·2019-08-30 15:48
閱讀 1127·2019-08-30 14:09
閱讀 511·2019-08-30 12:55
閱讀 2732·2019-08-29 11:29
閱讀 1490·2019-08-26 13:43
閱讀 2317·2019-08-26 13:30
閱讀 2373·2019-08-26 12:17