前端菜鳥一只想虎,查閱了一些資料窝革,大概明白了什么是函數(shù)節(jié)流
是什么姻蚓?
就是讓一個函數(shù)無法在很短的時間間隔內(nèi)連續(xù)執(zhí)行呻纹,只有當(dāng)上一次函數(shù)執(zhí)行后過了你規(guī)定的時間間隔堆生,才能進(jìn)行下一次該函數(shù)的調(diào)用。
為什么雷酪?
瀏覽器中某些計算和處理比其他要昂貴的多淑仆。例如,DOM操作比非DOM交互需要更多的內(nèi)存和CPU時間哥力。連續(xù)嘗試很多DOM操作會導(dǎo)致瀏覽器掛起甚至崩潰蔗怠。比如:重新調(diào)整瀏覽器窗口大卸胀洹(resize),瀏覽器頁面滾動(scroll)寞射,鼠標(biāo)移動(mousemove)渔工。也就是說用戶在觸發(fā)這些瀏覽器操作的時候,如果腳本里面綁定了對應(yīng)的事件處理方法桥温,這個方法就不停的觸發(fā)引矩。
應(yīng)用場景
如果函數(shù)節(jié)流根據(jù)應(yīng)用場景具體細(xì)分,還分兩種侵浸。
-
就是對于一定時間段的連續(xù)的函數(shù)調(diào)用脓魏,只讓其執(zhí)行一次回調(diào)函數(shù)。就是網(wǎng)上說的函數(shù)防抖(debounce)通惫。
- 每次 resize/scroll 觸發(fā)統(tǒng)計事件
- 文本輸入的驗證(連續(xù)輸入文字后發(fā)送 AJAX 請求進(jìn)行驗證茂翔,驗證一次就好)
-
讓一個函數(shù)不要執(zhí)行得太頻繁,減少一些過快的調(diào)用來節(jié)流履腋。我們需要間隔一定時間觸發(fā)回調(diào)來控制函數(shù)調(diào)用頻率就是網(wǎng)上說的函數(shù)節(jié)流(throttle)珊燎。
- DOM 元素的拖拽功能實現(xiàn)(mousemove)
射擊游戲的 mousedown/keydown 事件(單位時間只能發(fā)射一顆子彈) - 計算鼠標(biāo)移動的距離(mousemove)
- Canvas 模擬畫板功能(mousemove)
- 搜索聯(lián)想(keyup)
- 監(jiān)聽滾動事件判斷是否到頁面底部自動加載更多:給 scroll 加了 debounce 后,只有用戶停止?jié)L動后遵湖,才會判斷是否到了頁面底部悔政;如果是 throttle 的話,只要頁面滾動就會間隔一段時間判斷一次延旧。
- DOM 元素的拖拽功能實現(xiàn)(mousemove)
簡單實現(xiàn)
var timer;
function throttle(){
if(timer){
clearTimeout(timer);
console.log('3');
}
timer=setTimeout(function(){
console.log('函數(shù)防抖')
},1000);
}
for(var i=0;i<10;i++){
console.log("1");
throttle();
console.log('2');
}
上面的代碼其實就是一個函數(shù)防抖谋国,只執(zhí)行最后一次。
- 首先輸出1迁沫,然后第一次調(diào)用函數(shù)芦瘾,timer為undefined,所以不執(zhí)行if語句,創(chuàng)建一個定時器集畅,1秒后輸出‘函數(shù)防抖’近弟,timer有值,輸出2挺智,-
- 但是馬上又循環(huán)了一遍祷愉,輸出1,這回timer有值赦颇,但是清除了原先的定時器二鳄,輸出3,所以計時要重來媒怯,又創(chuàng)建一個,1秒后輸出‘函數(shù)防抖’订讼,timer有值,輸出2
- 接下來幾步和第二步相同沪摄,到最后一次throttle()函數(shù)調(diào)用的時候躯嫉,沒有再清除timer定時器的機會了纱烘,所以一秒后輸出‘函數(shù)防抖’。
接下來我們對整體進(jìn)行封裝
function throttle(fn, delay) {
var timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(function () {
fn(arguments)}, delay);
}
}
function fn() {
console.log("函數(shù)防抖");
}
var fn2 = throttle(fn, 1000);
fn2();
fn2();
fn2();
然后我們再次改進(jìn)變成函數(shù)節(jié)流祈餐,可以隔斷時間觸發(fā)回調(diào)函數(shù)
var throttle = function (fn, delay, atleast) {
var timer = null;
var previous = null;
return function () {
var now = +new Date();
if ( !previous ) previous = now;
if ( now - previous > atleast ) {
fn();
// 重置上一次開始時間為本次結(jié)束時間
previous = now;
} else {
clearTimeout(timer);
timer = setTimeout(function() {
fn();
}, delay);
}
}
};
參考資料:函數(shù)節(jié)流詳解