3 回答

TA貢獻(xiàn)1877條經(jīng)驗(yàn) 獲得超6個贊
問題中的代碼稍微改變了鏈接中的代碼。在鏈接中,檢查是否(immediate && !timeout)
在創(chuàng)建新的timout之前。擁有它后立即模式永遠(yuǎn)不會開火。我已經(jīng)更新了我的答案,以便從鏈接中注釋工作版本。
function debounce(func, wait, immediate) { // 'private' variable for instance // The returned function will be able to reference this due to closure. // Each call to the returned function will share this common timer. var timeout; // Calling debounce returns a new anonymous function return function() { // reference the context and args for the setTimeout function var context = this, args = arguments; // Should the function be called now? If immediate is true // and not already in a timeout then the answer is: Yes var callNow = immediate && !timeout; // This is the basic debounce behaviour where you can call this // function several times, but it will only execute once // [before or after imposing a delay]. // Each time the returned function is called, the timer starts over. clearTimeout(timeout); // Set the new timeout timeout = setTimeout(function() { // Inside the timeout function, clear the timeout variable // which will let the next execution run when in 'immediate' mode timeout = null; // Check if the function already ran with the immediate flag if (!immediate) { // Call the original function with apply // apply lets you define the 'this' object as well as the arguments // (both captured before setTimeout) func.apply(context, args); } }, wait); // Immediate mode and no wait timer? Execute the function.. if (callNow) func.apply(context, args); }}/////////////////////////////////// DEMO:function onMouseMove(e){ console.clear(); console.log(e.x, e.y);}// Define the debounced functionvar debouncedMouseMove = debounce(onMouseMove, 50);// Call the debounced function on every mouse movewindow.addEventListener('mousemove', debouncedMouseMove);

TA貢獻(xiàn)1811條經(jīng)驗(yàn) 獲得超6個贊
這里要注意的重要一點(diǎn)是debounce
產(chǎn)生一個“關(guān)閉” 變量的函數(shù)timeout
。timeout
在生成函數(shù)的每次調(diào)用期間,即使在debounce
返回之后,變量仍然可以訪問,并且可以在不同的調(diào)用之間進(jìn)行切換。
總體思路debounce
如下:
沒有超時開始。
如果調(diào)用生成的函數(shù),則清除并重置超時。
如果超時,請調(diào)用原始函數(shù)。
第一點(diǎn)是var timeout;
,它確實(shí)是公正的undefined
。幸運(yùn)的是,clearTimeout
它的輸入相當(dāng)松散:傳遞一個undefined
計(jì)時器標(biāo)識符會導(dǎo)致它什么也不做,它不會拋出錯誤或其他東西。
第二點(diǎn)由生成的函數(shù)完成。它首先在變量中存儲有關(guān)調(diào)用的一些信息(this
上下文和arguments
),以便稍后可以將它們用于去抖動調(diào)用。然后它清除超時(如果有一組),然后創(chuàng)建一個新的替換它使用setTimeout
。請注意,這會覆蓋timeout
多個函數(shù)調(diào)用的值,并且該值會持續(xù)存在!這允許去抖動實(shí)際工作:如果多次調(diào)用該函數(shù),timeout
則使用新的計(jì)時器多次覆蓋。如果不是這種情況,多次呼叫將導(dǎo)致啟動多個定時器,這些定時器都保持活動狀態(tài) - 呼叫只會被延遲,但不會被去抖動。
第三點(diǎn)是在超時回調(diào)中完成的。它取消設(shè)置timeout
變量并使用存儲的調(diào)用信息進(jìn)行實(shí)際的函數(shù)調(diào)用。
該immediate
標(biāo)志應(yīng)該控制是否應(yīng)該在定時器之前或之后調(diào)用該函數(shù)。如果是false
,則直到計(jì)時器被命中后才調(diào)用原始函數(shù)。如果是true
,則首先調(diào)用原始函數(shù),并且在計(jì)時器被命中之前不再調(diào)用它。
但是,我確實(shí)認(rèn)為if (immediate && !timeout)
檢查錯誤:timeout
剛剛設(shè)置為返回的計(jì)時器標(biāo)識符,setTimeout
因此!timeout
始終false
在該點(diǎn),因此永遠(yuǎn)不能調(diào)用該函數(shù)。當(dāng)前版本的underscore.js似乎有一個稍微不同的檢查,它immediate && !timeout
在調(diào)用之前進(jìn)行評估setTimeout
。(算法也有點(diǎn)不同,例如它不使用clearTimeout
。)這就是為什么你應(yīng)該總是嘗試使用最新版本的庫。:-)

TA貢獻(xiàn)1840條經(jīng)驗(yàn) 獲得超5個贊
去抖動函數(shù)在調(diào)用時不會執(zhí)行,它們會在執(zhí)行前等待一段可配置的持續(xù)時間暫停調(diào)用; 每次新調(diào)用都會重新啟動計(jì)時器。
限制函數(shù)執(zhí)行,然后等待可配置的持續(xù)時間,然后再次觸發(fā)。
去抖動非常適合按鍵事件; 當(dāng)用戶開始輸入然后暫停時,您將所有按鍵提交為單個事件,從而減少處理調(diào)用。
對于您只希望允許用戶在設(shè)定的一段時間內(nèi)調(diào)用一次的實(shí)時端點(diǎn),Throttle非常適合。
查看Underscore.js的實(shí)現(xiàn)。
添加回答
舉報