一起來(lái)聊個(gè)五毛錢(qián)的么,防抖與節(jié)流 實(shí)現(xiàn)與應(yīng)用
防抖與節(jié)流

導(dǎo)讀目錄
防抖(debounce)
防抖應(yīng)用場(chǎng)景
非立即執(zhí)行版
立即執(zhí)行版本
合成版本 防抖
節(jié)流
節(jié)流應(yīng)用場(chǎng)景
時(shí)間戳版本
定時(shí)器版本
總結(jié)
參考
1防抖(debounce)
所謂防抖,就是指觸發(fā)事件后 n 秒后才執(zhí)行函數(shù),如果在 n 秒內(nèi)又觸發(fā)了事件,則會(huì)重新計(jì)算函數(shù)執(zhí)行時(shí)間。
防抖類(lèi)型分為
非立即執(zhí)行版 立即執(zhí)行版 合成版本 防抖
防抖應(yīng)用場(chǎng)景
登錄、發(fā)短信等按鈕避免用戶(hù)點(diǎn)擊太快,以致于發(fā)送了多次請(qǐng)求 調(diào)整瀏覽器窗口大小時(shí),resize 次數(shù)過(guò)于頻繁,造成計(jì)算過(guò)多,此時(shí)需要一次到位 文本編輯器實(shí)時(shí)保存,當(dāng)無(wú)任何更改操作一秒后進(jìn)行保存
非立即執(zhí)行版
非立即執(zhí)行版的意思是觸發(fā)事件后函數(shù)不會(huì)立即執(zhí)行,而是在 n 秒后執(zhí)行,如果在 n 秒內(nèi)又觸發(fā)了事件,則會(huì)重新計(jì)算函數(shù)執(zhí)行時(shí)間。
/**
* @description:
* @param {*} func 觸發(fā)的時(shí)間
* @param {*} wait 多少時(shí)長(zhǎng)才執(zhí)行事件
* @return {*}
*/
function debounce(func, wait) {
let timeout;
return function(){
// 獲取當(dāng)前作用域和參數(shù)
const context = this;
const args = [...arguments]
// 如果當(dāng)前timeout 存在
// 清空定時(shí)器,再次等待wait時(shí)間過(guò)后再次執(zhí)行事件
if(timeout) clearTimeout(timeout)
// 定時(shí)執(zhí)行 傳遞進(jìn)來(lái)的事件
timeout = setTimeout(()=>{
func.apply(context,args)
},wait)
}
}
立即執(zhí)行版本
立即執(zhí)行版的意思是觸發(fā)事件后函數(shù)會(huì)立即執(zhí)行,然后 n 秒內(nèi)不觸發(fā)事件才能繼續(xù)執(zhí)行函數(shù)的效果。
function debounce(func,wait) {
let timeout;
return function () {
const context = this;
const args = [...arguments];
if (timeout) clearTimeout(timeout);
const callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
}
代碼解析
當(dāng) 執(zhí)行
debounce函數(shù)時(shí), 第一次進(jìn)來(lái)時(shí),timeout為false,所以callNow的值 為true,那么它會(huì)立即執(zhí)行func函數(shù),這時(shí)timeout的值 為true, 當(dāng)timeout 值為true 時(shí), 會(huì)執(zhí)行 清空定時(shí)器,此時(shí) timeout 又為 false 了, 這時(shí)callNow又 為true,再次執(zhí)行func函數(shù)。一直循環(huán)這樣的操作:
當(dāng)
timeout為false時(shí),會(huì)立刻執(zhí)行func函數(shù)。當(dāng)
timeout為true時(shí),它會(huì)執(zhí)行clearTimeOut,這時(shí)timeout又為false, 而callNow=! timeout, 就會(huì)立刻執(zhí)行func函數(shù)了。
合成版本 防抖
通過(guò)傳遞
Boolean來(lái)決定執(zhí)行哪種版本。
true為立即執(zhí)行版false為非立即執(zhí)行版本
debounce(func,1000,true)
/**
* @desc 函數(shù)防抖
* @param func 函數(shù)
* @param wait 延遲執(zhí)行毫秒數(shù)
* @param immediate true 表立即執(zhí)行,false 表非立即執(zhí)行
*/
function debounce(func, wait, immediate) {
let timeout;
return function () {
const context = this;
const args = [...arguments];
if (timeout) clearTimeout(timeout);
if (immediate) {
const callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(() => {
func.apply(context, args)
}, wait);
}
}
}
2節(jié)流
所謂節(jié)流,就是指連續(xù)觸發(fā)事件但是在 n 秒中只執(zhí)行一次函數(shù)。 節(jié)流會(huì)稀釋函數(shù)的執(zhí)行頻率。
節(jié)流有兩種實(shí)現(xiàn):
時(shí)間戳版本 定時(shí)器版本
節(jié)流應(yīng)用場(chǎng)景
scroll事件,每隔一秒計(jì)算一次位置信息等瀏覽器播放事件,每個(gè)一秒計(jì)算一次進(jìn)度信息等 input輸入框在搜索內(nèi)容時(shí),可以控制多少s 在執(zhí)行請(qǐng)求,避免多次發(fā)起請(qǐng)求,節(jié)約性能。
時(shí)間戳版本
function throttle(func, wait) {
var previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
定時(shí)器版本
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
代碼解析
當(dāng)執(zhí)行
throttle函數(shù)時(shí),timeout默認(rèn)為undefined, 此時(shí),! timeout為true時(shí),執(zhí)行 定時(shí)器,并且 將timeout為 null,即為false, 再次執(zhí)行throttle函數(shù)時(shí),!timeout又為true,再次執(zhí)行定時(shí)器。**通過(guò)
timeout的狀態(tài)來(lái)達(dá)到節(jié)流的控制 **
3總結(jié)
防抖:觸發(fā)事件后,一定時(shí)間后再執(zhí)行事件,可以
立即執(zhí)行也可以一定時(shí)間再執(zhí)行節(jié)流:控制流量,在單位時(shí)間內(nèi)只能請(qǐng)求一次,避免多次觸發(fā)事件,影響服務(wù)器性能。
參考
https://github.com/mqyqingfeng/Blog/issues/26
