剛寫完一篇debounce(防抖)函數的實現,我又看了下underscore.js的實現方法。算是趁熱打鐵,分析一下underscore裏實現的套路。app
先貼上源碼:函數
_.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { var last = _.now() - timestamp; console.log(last) if (last < wait && last >= 0) { console.log(1) timeout = setTimeout(later, wait - last); } else { console.log(2) timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; console.log(timeout) if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; };
一看可能有點多,我簡化一下,總體其實就兩部分:this
_.debounce = function( func, wait, immediate){ // 函數的回調部分 // 當immediate === false時 // func真正的執行部分 function later(){}; return function(){ // 在這裏判斷func是否當即執行 // 是否有計時器的存在 } }
上一篇文章已經分析過this的指向和event的傳遞,這裏就很少說了。直接來分析返回的匿名函數部分。code
return function() { context = this; args = arguments; // 這裏調用了underscore封裝的調用時間戳的方法 // 等同於 // timestamp = Date.now() timestamp = _.now(); var callNow = immediate && !timeout; console.log(timeout) if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } };
這裏我要說的是timestamp,它存儲的是動做發生時的時間戳,假設我這裏調用debounce時傳入的wait爲10000,也就是10秒。我第一次調用事件函數是在10:00:00,按照設定,10:00:10以後才能調用第二次方法,在這10秒內,任何調用都是不執行的。事件
當我第一次執行事件時underscore
timeout = undefined; immediate先設置爲false
因此回調函數
callNow === false
只有這句話是執行的源碼
if (!timeout) timeout = setTimeout(later, wait);
那接着來看later都有什麼:it
var later = function() { // var last = Date.now() - timestamp; var last = _.now() - timestamp; console.log(last) if (last < wait && last >= 0) { console.log(1) timeout = setTimeout(later, wait - last); } else { console.log(2) timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } };
在上一篇中,判斷wait內重複輸入,咱們取消事件的方法是這樣的io
if(timer){clearTimeout(timer)}
但在這裏,咱們是否是都還沒看到怎麼處理wait時間內,重複輸入無效的問題?別急,如今就來講。玄妙都在這個last變量上。
以前說過,timestamp存儲的是第一次事件執行時的時間戳(10:00:00),但如今我沒想等十秒,過了五秒我就觸發了第二次事件。因此timestamp如今的內容就變成新的時間戳了(10:00:05)。但問題是,timer的回調函數至少要到10:00:10以後纔會執行,也就是說
last>=5
因爲代碼執行堵塞致使last>10的狀況有可能存在,可是不符合咱們如今討論的,並且真的是太特殊了,咱們就不說了。那就假設last爲5秒(5000ms)。
last < wait && last >= 0
這句話就是true,那就執行裏面的代碼。但注意看裏面計時器對於時間的寫法。
wait - last
換個說法就是,你在10:00:00啓動了我,可是你在10:00:05又動了,我本來應該在10:00:10執行,可是如今懲罰你提早行動,那你以前等的時間就不算,你要再從新多等這幾秒10:00:15。
這個難點解決了,其餘就都好說。
lster剩餘的部分就是判斷若是當初設置的是當即執行(immediate = true),func就再也不執行一遍了,不然(immediate = false)func執行。
恩,那這個的解讀就結束了,有什麼地方我沒寫清楚的話,請給我留言。