剛寫完一篇debounce(防抖)函數(shù)的實(shí)現(xiàn),我又看了下underscore.js的實(shí)現(xiàn)方法涵叮。算是趁熱打鐵脂新,分析一下underscore里實(shí)現(xiàn)的套路。
先貼上源碼:
_.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;
};
};
一看可能有點(diǎn)多谒府,我簡化一下拼坎,整體其實(shí)就兩部分:
_.debounce = function( func, wait, immediate){
// 函數(shù)的回調(diào)部分
// 當(dāng)immediate === false時(shí)
// func真正的執(zhí)行部分
function later(){};
return function(){
// 在這里判斷func是否立即執(zhí)行
// 是否有計(jì)時(shí)器的存在
}
}
上一篇文章已經(jīng)分析過this的指向和event的傳遞浮毯,這里就不多說了。直接來分析返回的匿名函數(shù)部分泰鸡。
return function() {
context = this;
args = arguments;
// 這里調(diào)用了underscore封裝的調(diào)用時(shí)間戳的方法
// 等同于
// 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债蓝,它存儲(chǔ)的是動(dòng)作發(fā)生時(shí)的時(shí)間戳,假設(shè)我這里調(diào)用debounce時(shí)傳入的wait為10000盛龄,也就是10秒饰迹。我第一次調(diào)用事件函數(shù)是在10:00:00,按照設(shè)定余舶,10:00:10之后才能調(diào)用第二次方法啊鸭,在這10秒內(nèi),任何調(diào)用都是不執(zhí)行的匿值。
當(dāng)我第一次執(zhí)行事件時(shí)
timeout = undefined;
immediate先設(shè)置為false
所以
callNow === false
只有這句話是執(zhí)行的
if (!timeout) timeout = setTimeout(later, wait);
那接著來看later都有什么:
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內(nèi)重復(fù)輸入,我們?nèi)∠录姆椒ㄊ沁@樣的
if(timer){clearTimeout(timer)}
但在這里挟憔,我們是不是都還沒看到怎么處理wait時(shí)間內(nèi)钟些,重復(fù)輸入無效的問題?別急绊谭,現(xiàn)在就來說政恍。玄妙都在這個(gè)last變量上。
之前說過达传,timestamp存儲(chǔ)的是第一次事件執(zhí)行時(shí)的時(shí)間戳(10:00:00)抚垃,但現(xiàn)在我沒想等十秒,過了五秒我就觸發(fā)了第二次事件趟大。所以timestamp現(xiàn)在的內(nèi)容就變成新的時(shí)間戳了(10:00:05)鹤树。但問題是,timer的回調(diào)函數(shù)至少要到10:00:10之后才會(huì)執(zhí)行逊朽,也就是說
last>=5
由于代碼執(zhí)行堵塞導(dǎo)致last>10的情況有可能存在罕伯,但是不符合我們現(xiàn)在討論的,而且真的是太特殊了叽讳,我們就不說了追他。那就假設(shè)last為5秒(5000ms)。
last < wait && last >= 0
這句話就是true岛蚤,那就執(zhí)行里面的代碼邑狸。但注意看里面計(jì)時(shí)器對于時(shí)間的寫法。
wait - last
換個(gè)說法就是涤妒,你在10:00:00啟動(dòng)了我单雾,但是你在10:00:05又動(dòng)了,我原本應(yīng)該在10:00:10執(zhí)行,但是現(xiàn)在懲罰你提前行動(dòng)硅堆,那你之前等的時(shí)間就不算屿储,你要再重新多等這幾秒10:00:15。
這個(gè)難點(diǎn)解決了渐逃,其他就都好說够掠。
lster剩余的部分就是判斷如果當(dāng)初設(shè)置的是立即執(zhí)行(immediate = true),func就不再執(zhí)行一遍了茄菊,否則(immediate = false)func執(zhí)行疯潭。
恩,那這個(gè)的解讀就結(jié)束了面殖,有什么地方我沒寫清楚的話竖哩,請給我留言。