JavaScript的防抖和節(jié)流一起來了解下
1. 前言
首先來舉個例子。百度首頁的百度輸入框,用戶輸入的時候,每次輸入的信息,我們都能看到百度服務(wù)器返回給我們的聯(lián)想關(guān)鍵字。我們每改動一個字,它就換一次聯(lián)想詞,這是我們?nèi)庋勰芸吹降乃俣?,實際上如果不加以處理,可能已經(jīng)上服務(wù)器發(fā)起了好幾十次的同一個關(guān)鍵字聯(lián)想請求了,具體速度依賴于不同的pc等機器上的運行速度不同。那么,剛剛也談到,對于同一個關(guān)鍵字,請求這么多次,也許想給用戶呈現(xiàn)的就一次,剩下的請求都是浪費的,并且如果成千上萬甚至上億的用戶同時請求,對服務(wù)器的負(fù)擔(dān)是巨大的。
防抖節(jié)流解決的問題:
在連續(xù)觸發(fā)的事件中,事件處理函數(shù)的頻繁調(diào)用會加重瀏覽器或服務(wù)器的性能負(fù)擔(dān)導(dǎo)致用戶體驗糟糕,有哪些連續(xù)觸發(fā)的事件呢 ?
比如,瀏覽器滾動條的滾動事件、瀏覽器窗口調(diào)節(jié)的resize事件、輸入框內(nèi)容校驗以及在移動端的touchmove事件等。
所以,我們將采用防抖函數(shù)(debounce )和節(jié)流函數(shù)(throttle)來限制事件處理函數(shù)的調(diào)用頻率。
總的來說:防抖函數(shù)(debounce )和節(jié)流函數(shù)(throttle)是在時間軸上控制函數(shù)的執(zhí)行次數(shù)。
2. 函數(shù)防抖(debounce)
延遲防抖
延遲防抖(debounce): 在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計時。
生活中的實例: 如果有人進電梯(觸發(fā)事件),那電梯將在10秒鐘后出發(fā)(執(zhí)行事件監(jiān)聽器),這時如果又有人進電梯了(在10秒內(nèi)再次觸發(fā)該事件),我們又得等10秒再出發(fā)(重新計時)。
當(dāng)持續(xù)觸發(fā)事件時,一定時間段內(nèi)沒有再觸發(fā)事件,事件處理函數(shù)才會執(zhí)行一次。
如果設(shè)定的時間到來之前,又一次觸發(fā)了事件,就重新開始延時。
如下圖,持續(xù)觸發(fā)click事件時,并不執(zhí)行handle函數(shù),當(dāng)1000毫秒內(nèi)沒有觸發(fā)click事件時,才會延時觸發(fā)click事件。

前緣防抖
執(zhí)行動作在前,然后設(shè)定周期,周期內(nèi)有事件被觸發(fā),不執(zhí)行動作,且周期重新設(shè)定。
為什么要這樣呢?
試想第一種延遲debounce,我們本來想對用戶輸入的關(guān)鍵字,發(fā)起請求聯(lián)想的頻率降低,但是如果用戶在我們設(shè)定的時間中,一直輸入,導(dǎo)致的就是,用戶一直看不到關(guān)鍵字,我們倒不如第一次輸入的時候就發(fā)起一個請求,服務(wù)器返回結(jié)果,呈現(xiàn)給用戶,然后后續(xù)用戶的鍵入結(jié)束在繼續(xù)請求)。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>防抖</title>
</head>
<body>
<button id="debounce1">點我防抖吶!</button>
<script>
function handle() {
console.log("防抖成功!");
}
window.onload = function() {
// 1. 獲取按鈕,并綁定事件
var myDebounce = document.getElementById('debounce1');
myDebounce.addEventListener('click',debounce(handle,1000,true));
}
// 防抖函數(shù)
function debounce(fn,wait, immediate ){
//2. 設(shè)置時間戳,使用setTimeout讓返回函數(shù)延遲執(zhí)行
let timer, result;
return function(...args){
// 3. timer存在,將定時器中的函數(shù)清除
if(timer) clearTimeout(timer);
// 4.1 立即執(zhí)行返回函數(shù)
if(immediate){
if(!timer){
result = fn.apply(this,args);
}
timer = setTimeout(() => {
timer = null;
},wait);
}else{ // 4.2 非立即執(zhí)行返回函數(shù)
timer = setTimeout(() => {
fn.apply(this,args);
},wait);
}
}
// 5. 立即執(zhí)行時返回函數(shù)的返回值
return result;
}
</script>
</body>
</html>
實現(xiàn)效果:

原理解析:
- 防抖函數(shù)作用,對傳入的函數(shù)進行延時包裝后返回
- setTimeout在前一次未執(zhí)行完前,第二次次觸發(fā)將會覆蓋掉前面的定時器,執(zhí)行第二次的功能
- 前一次由于異步加延時還未執(zhí)行完,使用clearTimeout清除前面定時器,取消上次的fn功能
- 為保持fn內(nèi)部this的指向,使用apply改變this指向
- fn傳入為函數(shù),不是函數(shù)的調(diào)用
防抖函數(shù)實現(xiàn)總結(jié)
function debounce(fn,wait, immediate ){
//2. 設(shè)置時間戳,使用setTimeout讓返回函數(shù)延遲執(zhí)行
let timer, result;
return function(...args){
// 3. timer存在,將定時器中的函數(shù)清除
if(timer) clearTimeout(timer);
// 4.1 立即執(zhí)行返回函數(shù)
if(immediate){
if(!timer){
result = fn.apply(this,args);
}
timer = setTimeout(() => {
timer = null;
},wait);
}else{ // 4.2 非立即執(zhí)行返回函數(shù)
timer = setTimeout(() => {
fn.apply(this,args);
},wait);
}
}
// 5. 立即執(zhí)行時返回函數(shù)的返回值
return result;
}
3. 函數(shù)節(jié)流(throttling)
throttling,節(jié)流的策略是,固定周期內(nèi),只執(zhí)行一次動作,若有新事件觸發(fā),不執(zhí)行。周期結(jié)束后,又有事件觸發(fā),開始新的周期。 節(jié)流策略也分前緣和延遲兩種。
與debounce類似,延遲是指 周期結(jié)束后執(zhí)行動作,前緣是指執(zhí)行動作后再開始周期。
- 節(jié)流會稀釋函數(shù)的執(zhí)行頻率
- 在持續(xù)觸發(fā)事件的過程中,函數(shù)會立即執(zhí)行,并且每n秒執(zhí)行一次
生活中的實例: 我們知道目前的一種說法是當(dāng) 1 秒內(nèi)連續(xù)播放 24 張以上的圖片時,在人眼的視覺中就會形成一個連貫的動畫,所以在電影的播放(以前是,現(xiàn)在不知道)中基本是以每秒 24 張的速度播放的,為什么不 100 張或更多是因為 24 張就可以滿足人類視覺需求的時候,100 張就會顯得很浪費資源
延遲節(jié)流

前緣節(jié)流

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>節(jié)流</title>
</head>
<body>
<button id="debounce1">點我節(jié)流吶!</button>
<script>
function handle() {
console.log("節(jié)流成功!");
}
window.onload = function() {
var myDebounce = document.getElementById('debounce1');
myDebounce.addEventListener('click',throttling(handle,1000,false));
}
// 節(jié)流函數(shù)
function throttling(fn,wait,immediate){
let timer;
return function(...args) {
if(!timer){
if(immediate){
fn.apply(this,args);
}
timer = setTimeout(() => {
if(!immediate) {
fn.apply(this,args);
}
timer = null;
},wait);
}
}
}
</script>
</body>
</html>
實現(xiàn)效果:

節(jié)流函數(shù)實現(xiàn)總結(jié)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>節(jié)流</title>
</head>
<body>
<button id="debounce1">點我節(jié)流吶!</button>
<script>
function handle() {
console.log("節(jié)流成功!");
}
window.onload = function() {
var myDebounce = document.getElementById('debounce1');
myDebounce.addEventListener('click',throttling(handle,1000,false));
}
// 節(jié)流函數(shù)
function throttling(fn,wait,immediate){
let timer;
return function(...args) {
if(!timer){
if(immediate){
fn.apply(this,args);
}
timer = setTimeout(() => {
if(!immediate) {
fn.apply(this,args);
}
timer = null;
},wait);
}
}
}
</script>
</body>
</html>
4. 兩者區(qū)別
函數(shù)節(jié)流不管事件觸發(fā)有多頻繁,都會保證在規(guī)定時間內(nèi)一定會執(zhí)行一次真正的事件處理函數(shù)。
函數(shù)防抖只是在最后一次事件后才觸發(fā)一次函數(shù)。
比如在頁面的無限加載場景下,我們需要用戶在滾動頁面時,每隔一段時間發(fā)一次 Ajax 請求,而不是在用戶停下滾動頁面操作時才去請求數(shù)據(jù)。這樣的場景,就適合用節(jié)流技術(shù)來實現(xiàn)。
5. 應(yīng)用場景
對于函數(shù)防抖,有以下幾種應(yīng)用場景:
- 給按鈕加函數(shù)防抖防止表單多次提交。
- 對于輸入框連續(xù)輸入進行AJAX驗證時,用函數(shù)防抖能有效減少請求次數(shù)。
- 判斷scroll是否滑到底部,滾動事件+函數(shù)防抖
總的來說,適合多次事件一次響應(yīng)的情況
對于函數(shù)節(jié)流,有如下幾個場景:
- 游戲中的刷新率
- DOM元素拖拽
- Canvas畫筆功能
總的來說,適合大量事件按時間做平均分配觸發(fā)。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
JavaScript實現(xiàn)的級聯(lián)算法示例【省市二級聯(lián)動功能】
這篇文章主要介紹了JavaScript實現(xiàn)的級聯(lián)算法,結(jié)合省市二級聯(lián)動下拉菜單功能實例分析了javascript事件響應(yīng)與元素動態(tài)操作實現(xiàn)級聯(lián)算法的相關(guān)技巧,需要的朋友可以參考下2018-12-12
javascript實現(xiàn)點擊產(chǎn)生隨機圖形
這篇文章主要為大家詳細介紹了javascript實現(xiàn)點擊產(chǎn)生隨機圖形,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-01-01
將rmb數(shù)字位錢數(shù)轉(zhuǎn)化為中文錢數(shù)的js函數(shù)
用力將用戶輸入的錢數(shù),中文化輸出的函數(shù)代碼,需要的朋友可以參考下。2010-07-07
JavaScript 正則表達式中g(shù)lobal模式的特性
這篇文章主要介紹了JavaScript 正則表達式中g(shù)lobal模式的特性 的相關(guān)資料,需要的朋友可以參考下2016-02-02
Html+CSS+JS輪播圖實現(xiàn)源碼(手動輪播,自動輪播)
今天做網(wǎng)站的時候需要用上JS輪播圖代碼,而且還要求是原生的JS代碼,下面這篇文章主要給大家介紹了關(guān)于Html+CSS+JS輪播圖實現(xiàn)的相關(guān)資料,文中介紹的方法包括手動輪播和自動輪播,需要的朋友可以參考下2023-06-06

